Java中ArrayList remove()的不良行为

时间:2015-05-20 12:27:54

标签: java collections

我有以下两种情况:

1。 int值作为参数

int intNum = 2;
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.remove(intNum);
System.out.println(list.size());
// output: 2

2。长值作为参数

long longNum = 2;
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.remove(longNum);
System.out.println(list.size());
// output: 3

我在两种情况下都传递了2作为值,但是我获得了List的不同大小值。这种行为的实际原因是什么?

Properly removing an Integer from a List没有解释内置数据类型具有相同的值但行为与上面提到的不同

7 个答案:

答案 0 :(得分:25)

<强>自动装箱

list.remove方法重载,两个不同的签名用于不同的目的。一个list.remove(int)根据索引删除项目,另一个list.remove(Object)根据对象相等性删除项目。 您的第一个情况会触发第一个类型,而您的第二个示例(使用long longNum)会触发第二个类型,将long原语自动装箱到java.lang.Long个对象。这不等于添加到列表中的java.lang.Integer(自动装箱)值,因此不会从列表中删除任何内容,并且大小将保持不变。

答案 1 :(得分:5)

来自List.remove()documentation

  

remove(int index)移除指定位置的元素   这个清单(可选操作)。

     

remove(Object o)删除指定元素的第一个匹配项   从该列表中,如果它存在(可选操作)。

     

removeAll(Collection c)从此列表中删除其所有元素   包含在指定集合中(可选操作)。

如果您的第二个示例确实很长,则不会被删除(因为它使用了第二个删除方法)。

答案 2 :(得分:5)

小心:第一个删除Integer处的index = 2。见ArrayList.remove(int)

第二个尝试使用ArrayList.remove(Object)删除对象,但您要删除的对象不存在,因为它是Long对象。

答案 3 :(得分:4)

List界面包含两种remove()方法 - remove(Object)remove(int)

Java 6中remove(Object)的实现如下:

public boolean remove(Object o) {
if (o == null) {
        for (int index = 0; index < size; index++)
    if (elementData[index] == null) {
        fastRemove(index);
        return true;
    }
} else {
    for (int index = 0; index < size; index++)
    if (o.equals(elementData[index])) {
        fastRemove(index);
        return true;
    }
    }
return false;
}

Java 6中remove(int)的实现是:

public E remove(int index) {
RangeCheck(index);

modCount++;
E oldValue = (E) elementData[index];

int numMoved = size - index - 1;
if (numMoved > 0)
    System.arraycopy(elementData, index+1, elementData, index,
             numMoved);
elementData[--size] = null; // Let gc do its work

return oldValue;
}

在第一个示例中,您实际上正在调用remove(int)方法,该方法会删除指定索引处的对象。在这种情况下,您指定了索引2,实际上是值“3”。

在您的第二个示例中,您调用remove(Object)方法,因为没有remove(long)方法,long将不会转换为int }。基于remove(Object)方法的实现,它查找对象相等性。由于您的列表包含Integer类型的对象,并且您提供了Long,因此没有任何内容可以与之匹配。

以下方法可能是正在发生的事情的一个更好的例子:

public static void main(String[] args) {
    ArrayList<Integer> list;

    System.out.println("Removing intNum");
    int intNum = 2;
    list = new ArrayList<Integer>();
    list.add(1);
    list.add(2);
    list.add(3);
    System.out.println("List = " + list);
    list.remove(intNum);
    System.out.println("List = " + list);

    System.out.println("Removing longNum");
    long longNum = 2;
    list = new ArrayList<Integer>();
    list.add(1);
    list.add(2);
    list.add(3);
    System.out.println("List = " + list);
    list.remove(longNum);
    System.out.println("List = " + list);
}

此代码的输出为:

Removing intNum
List = [1, 2, 3]
List = [1, 2]
Removing longNum
List = [1, 2, 3]
List = [1, 2, 3]

答案 4 :(得分:3)

使用int参数调用List.remove()将匹配签名remove(int index),并且无论列表是否具有值为2的Integer项,都将删除索引list[2]上的项。

使用long参数调用List.remove()将导致编译器对Long对象执行自动装箱并匹配签名remove(Object o)。列表将迭代,询问o.equals(每个项目)和之前提到的,Long不等于Integer。

答案 5 :(得分:2)

当您执行list.remove(intNum);时,它正在执行remove(int index);类的List签名,该签名将删除给定索引的项目。

但是当你list.remove(longNum);(考虑到你的意思是longNumlong)时,它会执行boolean remove(Object o);List签名,检查是否该对象存在于列表中,如果是,则将其删除。

由于列表是Integer列表,因此找不到对象,也没有删除任何内容,这就是为什么第二个结果是3,没有删除任何内容。

答案 6 :(得分:2)

Collection中的List有两个重载方法 1. public E remove(int index) 2. public boolean remove(Object o)

在你的情况下,第二种方法被调用。因为你传递的时间很长(隐式转换为长包装器类所以对象类)。

现在List.remove(longNum)删除具有最低索引i的元素,使得(longNum == null?get(i)== null:longNum.equals(get(i)))(如果存在这样的元素) )。

现在在你的情况下,当计数器来到第二个位置(即i = 1)。 longNum.equals(get(1))返回 false ,因为get(1)返回值为= 2的java.lang.Integer的Object,longNum是java.lang.Long的一个实例,其值为= 2。所以equals方法返回false,因此它不会删除元素。

您可以按类名称

检查值的类型
char