Java中ArrayList remove()的不良行为[重复]
Posted
技术标签:
【中文标题】Java中ArrayList remove()的不良行为[重复]【英文标题】:Undesired behavior of ArrayList remove() in Java [duplicate] 【发布时间】:2015-08-01 16:47:18 【问题描述】:我有以下两种情况:
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 作为值传递,但我得到的列表大小值不同。这种行为的真正原因是什么?
Properly removing an Integer from a List 没有解释关于具有相同值但行为不同的内置数据类型,如上所述
【问题讨论】:
现在您看到 list.remove(2) 正在删除索引 2 处的元素而不是整数 2,您应该指出明确转换为“int”或“Integer”,否则您只会冒险迷惑自己。 这可能是一个技术面试的好问题。 【参考方案1】:自动装箱
list.remove
方法被重载,两个不同的签名用于不同的目的。一个,list.remove(int)
,根据其索引删除一个项目,另一个,list.remove(Object)
,根据对象相等性删除一个项目。
您的第一种情况触发第一种类型,而您的第二个示例(使用long longNum
)触发第二种类型,将long
原语自动装箱为java.lang.Long
对象。这不等于添加到列表中的java.lang.Integer
(自动装箱)值,因此不会从列表中删除任何内容并且大小将保持不变。
【讨论】:
而第二种情况之所以选择Object
重载是因为long
不能转换为int
。 Object
重载是唯一可以申请的,
即使第一种情况触发了第二种情况,结果也会不一样。【参考方案2】:
来自 List.remove() documentation :
remove(int index) 删除指定位置的元素 此列表(可选操作)。
remove(Object o) 删除指定元素的第一个匹配项 从这个列表中,如果它存在(可选操作)。
removeAll(Collection c) 从此列表中删除其所有元素 包含在指定集合中(可选操作)。
如果您的第二个示例确实很长,则不会将其删除(因为它使用了第二个删除方法)。
【讨论】:
【参考方案3】:小心:第一个删除了index = 2
处的Integer
。见ArrayList.remove(int)
第二个尝试使用ArrayList.remove(Object)
删除对象,但您要删除的对象不存在,因为它是Long
对象。
【讨论】:
【参考方案4】:List
接口包含两个 remove()
方法 - remove(Object)
和 remove(int)
。
remove(Object)
在Java 6中的实现如下:
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;
remove(int)
在Java 6中的实现是:
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]
【讨论】:
【参考方案5】:使用 int 参数调用 List.remove() 将匹配签名 remove(int index)
并且索引 list[2]
上的项目将被删除,无论列表是否具有值为 2 的 Integer 项目。
使用 long 参数调用 List.remove() 将导致编译器自动装箱到 Long 对象中并匹配签名 remove(Object o)
。该列表将迭代,询问是否 o.equals(each item) 并且如前所述,Long 不等于 Integer。
【讨论】:
【参考方案6】:当您执行list.remove(intNum);
时,它正在执行List
类的remove(int index);
签名,这会删除给定索引的项目。
但是,当您执行 list.remove(longNum);
(考虑到您的意思是 longNum
是 long
)时,它执行了 List
类的 boolean remove(Object o);
签名,它检查对象是否存在于列表中,以及是否是的,删除它。
由于列表是Integer
列表,它找不到对象并且没有删除任何内容,这就是为什么第二个结果是 3,所以没有删除任何内容。
【讨论】:
【参考方案7】:Collection 中的List 有两个重载方法 1. public E remove(int index) 2. public boolean remove(Object o)
在您的情况下,第二种方法被调用。因为您正在传递 long(隐式转换为 Long wrapper 类所以 Object 类)。
现在 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 的对象,而 longNum 是值 = 的 java.lang.Long 的一个实例2.所以 equals 方法返回 false 因此它不会删除元素。
你可以通过类名检查值的类型
System.out.println(" Element Value :"+list.get(1));
System.out.println(" Element Class :"+list.get(1).getClass());
【讨论】:
以上是关于Java中ArrayList remove()的不良行为[重复]的主要内容,如果未能解决你的问题,请参考以下文章
Java ArrayList.remove() 不起作用? [复制]
关于java里ArrayList类的remove(index)方法
Java Arraylist.remove 删除第一次出现而不是索引,即使我输入了一个 int? [复制]