从列表<Integer>中正确删除一个整数


201

这是我刚遇到的一个陷阱。考虑一个整数列表:

List<Integer> list = new ArrayList<Integer>();
list.add(5);
list.add(6);
list.add(7);
list.add(1);

关于执行时会发生什么的任何有根据的猜测list.remove(1)?那list.remove(new Integer(1))呢 这可能会导致一些令人讨厌的错误。

在处理整数列表时,区分remove(int index)从给定索引remove(Object o)中删除元素的和,按引用删除元素的正确方法是什么?


这里要考虑的重点是提到@Nikita-精确参数匹配优先于自动装箱。


11
答:真正的问题在于,Sun的某人以某种方式认为具有围绕原语的(不可变的)包装器类是聪明的,后来又有人以为具有自动装箱(unboxing)的方法甚至更聪明...而且人们保持使用LAME DEFAULT API存在更好的情况。对于许多目的,有一种new Arraylist <Integer> 更好的解决方案。例如Trove提供了一个TIntArrayList。我使用Java编程的次数越多(自2001年以来为SCJP),使用包装类的次数就越少,使用经过精心设计的API(想到的是Trov,Google等)也就越多。
语法T3rr0r,2010年

Answers:


230

Java始终会调用最适合您的参数的方法。仅在没有不使用强制转换/自动装箱方法就无法调用的方法的情况下,才执行自动装箱和隐式向上转换。

List接口指定两个remove方法(请注意参数的命名):

  • remove(Object o)
  • remove(int index)

这意味着list.remove(1)删除位置1的对象,并remove(new Integer(1))从该列表中删除指定元素的首次出现。


110
采摘尼特:Integer.valueOf(1)比更好的做法new Integer(1)。静态方法可以进行缓存等操作,因此您将获得更好的性能。
decitrig 2011年

彼得·劳瑞(Peter Lawrey)的建议更好,并且避免了不必要的对象创建。
assylias 2013年

@assylias:Peter Lawrey的提议与decitrig的提议完全相同,只是透明度较低。
马克·彼得斯

@MarkPeters我的评论与有关new Integer(1),但我同意Integer.valueOf(1)(Integer) 1等同。
assylias 2013年

68

您可以使用铸造

list.remove((int) n);

list.remove((Integer) n);

不管n是int还是Integer,该方法将始终调用您期望的值。

使用(Integer) nInteger.valueOf(n)new Integer(n)前两个可以使用Integer缓存更有效,而后者则将始终创建对象。


2
如果您能解释为什么会这样,那将是很好的:) [自动装箱条件...]
Yuval Adam 2010年

通过使用强制转换,可以确保编译器看到所需的类型。在第一种情况下'(int)n'只能是int类型,在第二种情况下'(Integer)n'只能是Integer类型。'n'将根据需要进行转换/装箱/取消装箱,否则将产生编译器错误。
彼得·劳里

10

我不知道“正确”的方式,但是您建议的方式就可以了:

list.remove(int_parameter);

删除给定位置的元素,然后

list.remove(Integer_parameter);

从列表中删除给定的对象。

这是因为VM首先尝试查找使用完全相同的参数类型声明的方法,然后才尝试自动装箱。


7

list.remove(4)是的完全匹配list.remove(int index),因此将被调用。如果您要致电,请list.remove(Object)执行以下操作:list.remove((Integer)4)


谢谢皮塔尔(Petar),对您来说,(Integer)像您上面写的那样简单的转换似乎是最简单的方法。
vikingsteve

使用最后一种方法时,似乎返回一个布尔值。当尝试堆叠多个移除时,我得到一个错误,即我无法在布尔值上调用移除。
Bram Vanroy 2015年

4

关于执行list.remove(1)时会发生什么的任何有根据的猜测?那么list.remove(new Integer(1))呢?

无需猜测。第一种情况将导致List.remove(int)被调用,并且该位置上的元素1将被删除。第二种情况将导致List.remove(Integer)被调用,并且值等于的元素Integer(1)将被删除。在这两种情况下,Java编译器都会选择最接近的匹配重载。

是的,这里可能会造成混乱(和错误),但这是一个非常罕见的用例。

List.remove在Java 1.2中定义了这两种方法时,重载并不是模棱两可的。该问题仅是由于Java 1.5中引入了泛型和自动装箱而引起的。事后看来,如果为其中一个remove方法指定不同的名称会更好。但是现在为时已晚。


2

请注意,即使VM没有做正确的事,您仍然可以通过使用remove(java.lang.Object)对任意对象进行操作的事实来确保正确的行为:

myList.remove(new Object() {
  @Override
  public boolean equals(Object other) {
    int k = ((Integer) other).intValue();
    return k == 1;
  }
}

该“解决方案”打破了equals方法的约定,特别是(来自Javadoc)“它是对称的:对于x和y的任何非空引用值,x.equals(y)当且仅当y.equals( x)返回true。”。因此,不能保证在的所有实现上都能工作List,因为List的任何实现都可以随意交换x和y x.equals(y),因为的Javadoc Object.equals说这应该是有效的。
Erwin Bolwidt

1

简而言之,我喜欢按照#decitrig的建议在接受的答案第一条评论中进行关注。

list.remove(Integer.valueOf(intereger_parameter));

这对我有帮助。再次感谢#decitrig您的评论。它可能对某些人有帮助。


0

好吧,这就是窍门。

让我们在这里举两个例子:

public class ArrayListExample {

public static void main(String[] args) {
    Collection<Integer> collection = new ArrayList<>();
    List<Integer> arrayList = new ArrayList<>();

    collection.add(1);
    collection.add(2);
    collection.add(3);
    collection.add(null);
    collection.add(4);
    collection.add(null);
    System.out.println("Collection" + collection);

    arrayList.add(1);
    arrayList.add(2);
    arrayList.add(3);
    arrayList.add(null);
    arrayList.add(4);
    arrayList.add(null);
    System.out.println("ArrayList" + arrayList);

    collection.remove(3);
    arrayList.remove(3);
    System.out.println("");
    System.out.println("After Removal of '3' :");
    System.out.println("Collection" + collection);
    System.out.println("ArrayList" + arrayList);

    collection.remove(null);
    arrayList.remove(null);
    System.out.println("");
    System.out.println("After Removal of 'null': ");
    System.out.println("Collection" + collection);
    System.out.println("ArrayList" + arrayList);

  }

}

现在让我们看一下输出:

Collection[1, 2, 3, null, 4, null]
ArrayList[1, 2, 3, null, 4, null]

After Removal of '3' :
Collection[1, 2, null, 4, null]
ArrayList[1, 2, 3, 4, null]

After Removal of 'null': 
Collection[1, 2, 4, null]
ArrayList[1, 2, 3, 4]

现在让我们分析输出:

  1. 从集合中删除3时,它将调用以remove()方法Object o为参数的集合方法。因此,它删除了对象3。但是在arrayList对象中,它被索引3覆盖,因此删除了第4个元素。

  2. 通过对象删除的相同逻辑,在第二种输出中的两种情况下都将删除null。

因此,要删除3作为对象的数字,我们将明确需要将3传递为object

这可以通过使用wrapper类进行转换或包装来完成Integer

例如:

Integer removeIndex = Integer.valueOf("3");
collection.remove(removeIndex);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.