Java 中 ArrayList remove() 的不良行为
Undesired behavior of ArrayList remove() in Java
我有以下两种情况:
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() documentation :
remove(int index) Removes the element at the specified position in
this list (optional operation).
remove(Object o) Removes the first occurrence of the specified element
from this list, if it is present (optional operation).
removeAll(Collection c) Removes from this list all of its elements
that are contained in the specified collection (optional operation).
如果你的第二个例子确实很长,它不会被删除(因为它使用了第二个删除方法)。
List
接口包含两个 remove()
方法 - remove(Object)
和 remove(int)
。
Java6中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;
}
Java6中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]
自动装箱
重载了list.remove
方法,两个不同的签名用于不同的目的。一个 list.remove(int)
根据其索引删除项目,另一个 list.remove(Object)
根据对象相等性删除项目。
您的第一种情况触发第一种类型,您的第二个示例(使用 long longNum
)触发第二种类型,将 long
基元自动装箱到 java.lang.Long
对象。这不等于添加到列表中的 java.lang.Integer
(自动装箱)值,因此不会从列表中删除任何内容,大小将保持不变。
注意:第一个删除 index = 2
处的 Integer
。参见 ArrayList.remove(int)
第二个尝试删除带有 ArrayList.remove(Object)
的对象,但您要删除的对象不存在,因为它是 Long
对象。
当您执行 list.remove(intNum);
时,它正在执行 List
class 的 remove(int index);
签名,这会删除给定索引的项目。
但是,当您执行 list.remove(longNum);
时(考虑到您的意思是 longNum
成为 long
),它执行了 List
[=25] 的 boolean remove(Object o);
签名=],检查对象是否存在于列表中,如果是,则将其删除。
由于列表是一个 Integer
列表,它找不到对象并且没有删除任何内容,这就是为什么第二个结果是 3,所以没有删除任何内容。
使用 int 参数调用 List.remove() 将匹配签名 remove(int index)
并且索引 list[2]
上的项目将被删除,无论列表是否具有值为的 Integer 项目2.
使用长参数调用 List.remove() 将导致编译器自动装箱到长对象中并匹配签名 remove(Object o)
。该列表将迭代,询问是否 o.equals(每个项目)并且如前所述,Long 不等于 Integer。
集合中的列表有两个重载方法
1. public E 删除(整数索引)
2. public 布尔删除(对象 o)
在您的情况下,第二个方法 被调用。因为你传递的是 long (隐式转换为 Long wrapper class 所以 Object class).
现在 List.remove(longNum) 删除具有最低索引 i 的元素,这样 (longNum==null ? get(i)==null : longNum.equals(get(i) ) ) (如果存在这样的元素)。
现在在你的例子中,计数器排在第二位(即 i=1)。 longNum.equals(get(1)) returns false 因为 get(1) returns java.lang.Integer 的对象,值为 2 和longNum 是 java.lang.Long 的实例,其值为 2。所以等于方法 returns false 因此它不会删除元素。
您可以通过其class名称
检查值的类型
System.out.println(" Element Value :"+list.get(1));
System.out.println(" Element Class :"+list.get(1).getClass());
我有以下两种情况:
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() documentation :
remove(int index) Removes the element at the specified position in this list (optional operation).
remove(Object o) Removes the first occurrence of the specified element from this list, if it is present (optional operation).
removeAll(Collection c) Removes from this list all of its elements that are contained in the specified collection (optional operation).
如果你的第二个例子确实很长,它不会被删除(因为它使用了第二个删除方法)。
List
接口包含两个 remove()
方法 - remove(Object)
和 remove(int)
。
Java6中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;
}
Java6中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]
自动装箱
重载了list.remove
方法,两个不同的签名用于不同的目的。一个 list.remove(int)
根据其索引删除项目,另一个 list.remove(Object)
根据对象相等性删除项目。
您的第一种情况触发第一种类型,您的第二个示例(使用 long longNum
)触发第二种类型,将 long
基元自动装箱到 java.lang.Long
对象。这不等于添加到列表中的 java.lang.Integer
(自动装箱)值,因此不会从列表中删除任何内容,大小将保持不变。
注意:第一个删除 index = 2
处的 Integer
。参见 ArrayList.remove(int)
第二个尝试删除带有 ArrayList.remove(Object)
的对象,但您要删除的对象不存在,因为它是 Long
对象。
当您执行 list.remove(intNum);
时,它正在执行 List
class 的 remove(int index);
签名,这会删除给定索引的项目。
但是,当您执行 list.remove(longNum);
时(考虑到您的意思是 longNum
成为 long
),它执行了 List
[=25] 的 boolean remove(Object o);
签名=],检查对象是否存在于列表中,如果是,则将其删除。
由于列表是一个 Integer
列表,它找不到对象并且没有删除任何内容,这就是为什么第二个结果是 3,所以没有删除任何内容。
使用 int 参数调用 List.remove() 将匹配签名 remove(int index)
并且索引 list[2]
上的项目将被删除,无论列表是否具有值为的 Integer 项目2.
使用长参数调用 List.remove() 将导致编译器自动装箱到长对象中并匹配签名 remove(Object o)
。该列表将迭代,询问是否 o.equals(每个项目)并且如前所述,Long 不等于 Integer。
集合中的列表有两个重载方法 1. public E 删除(整数索引) 2. public 布尔删除(对象 o)
在您的情况下,第二个方法 被调用。因为你传递的是 long (隐式转换为 Long wrapper class 所以 Object class).
现在 List.remove(longNum) 删除具有最低索引 i 的元素,这样 (longNum==null ? get(i)==null : longNum.equals(get(i) ) ) (如果存在这样的元素)。
现在在你的例子中,计数器排在第二位(即 i=1)。 longNum.equals(get(1)) returns false 因为 get(1) returns java.lang.Integer 的对象,值为 2 和longNum 是 java.lang.Long 的实例,其值为 2。所以等于方法 returns false 因此它不会删除元素。
您可以通过其class名称
检查值的类型 System.out.println(" Element Value :"+list.get(1));
System.out.println(" Element Class :"+list.get(1).getClass());