相同的方法调用,LinkedList、Queue 与 List 中的不同结果
Same method invocations, different result in LinkedList, Queue vs. List
我已阅读线程 Difference in LinkedList, queue vs list,但仍然不理解以下代码行为:
List<Integer> l = new LinkedList<>();
l.add(10);
l.add(12);
l.remove(1); // removes index 1
System.out.println(l); // outputs [10]
Queue<Integer> q2 = new LinkedList<>();
q2.add(10);
q2.add(12);
q2.remove(1); // tries to remove object 1
System.out.println(q2); // outputs [10, 12]
我知道,为什么输出不同:
- 在
l
的情况下,调用 remove(1)
删除索引为 1
的元素
- 在
q
的情况下,调用 remove(1)
试图删除对象 1
,但只有整数 10
和 12
。
如果我仔细看看接口:
- Queue does not have a
remove(int)
method, but inherits a remove(Object)
method from Collection.
- List 有一个
remove(int index)
和 remove(Object o)
方法。
到目前为止,还不错。
但是:我不明白,Java 如何能够在使用相同语法 (remove(1)
) 的同时调用两种不同的方法?
这两种方法的实现是不同的 -- 因为 Queue 只允许从堆栈的底部和顶部移除,所以您必须对这两种移除方法使用特定的方法,而 List 则意味着对于索引运算符。
在这种情况下,队列覆盖 remove(Object obj)
以删除对象本身,而列表则覆盖 remove(Object obj)
并添加额外的 remove(int index)
以允许删除索引和对象。
对于List
,编译器根据method signature选择remove(int)
:
The process of determining applicability begins by determining the potentially applicable methods (§15.12.2.1). Then, to ensure compatibility with the Java programming language prior to Java SE 5.0, the process continues in three phases:
- The first phase performs overload resolution without permitting boxing or unboxing conversion, or the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the second phase.
注意强调的部分,这意味着编译器将 1
与 int
匹配。由于有一个适用的方法 remove(int)
,因此选择了该方法进行调用。换句话说,首先考虑原始类型,然后再考虑引用类型,例如 Integer
(int
的装箱类型)。
你的主要问题是调用代码。有
l.remove(1);
您正在使用 int
作为通用数据结构的参数,该数据结构是使用 Integer
作为类型参数声明的。这也适用于队列,因为 Java.
中的自动装箱行为
如果您使用 Integer
,则两个调用的行为相同:
l.remove(Integer.valueOf(1));
q2.remove(Integer.valueOf(1));
现在这两个调用都删除了所需的 对象。
在你的例子中 l
是类型 List
,它有两个 remove
方法:remove(Object)
继承自 Collection
和 remove(int)
为 List
定义。对 remove(1)
的调用链接到 remove(int)
,因为这是完全匹配。
另一方面,q2
是 Queue
并且此类型未指定 remove(int)
。只有 remove(Object)
来自 Collection
。因此编译器生成从 int
到 Integer
的自动装箱并将调用链接到 remove(Object)
.
可能令人困惑的是:q2 实际上持有 List
。但这没关系。编译器只“看到”引用对象的变量类型。 Object 的真实类型被忽略(在大多数情况下实际上是未知的)。
知道 q2 包含 List
你可以进行类型转换:
((List<Integer>)q2).remove(1);
这将再次导致对 List.remove(int)
的调用,因为编译器随后知道您正在处理的对象是 List
我已阅读线程 Difference in LinkedList, queue vs list,但仍然不理解以下代码行为:
List<Integer> l = new LinkedList<>();
l.add(10);
l.add(12);
l.remove(1); // removes index 1
System.out.println(l); // outputs [10]
Queue<Integer> q2 = new LinkedList<>();
q2.add(10);
q2.add(12);
q2.remove(1); // tries to remove object 1
System.out.println(q2); // outputs [10, 12]
我知道,为什么输出不同:
- 在
l
的情况下,调用remove(1)
删除索引为1
的元素
- 在
q
的情况下,调用remove(1)
试图删除对象1
,但只有整数10
和12
。
如果我仔细看看接口:
- Queue does not have a
remove(int)
method, but inherits aremove(Object)
method from Collection. - List 有一个
remove(int index)
和remove(Object o)
方法。
到目前为止,还不错。
但是:我不明白,Java 如何能够在使用相同语法 (remove(1)
) 的同时调用两种不同的方法?
这两种方法的实现是不同的 -- 因为 Queue 只允许从堆栈的底部和顶部移除,所以您必须对这两种移除方法使用特定的方法,而 List 则意味着对于索引运算符。
在这种情况下,队列覆盖 remove(Object obj)
以删除对象本身,而列表则覆盖 remove(Object obj)
并添加额外的 remove(int index)
以允许删除索引和对象。
对于List
,编译器根据method signature选择remove(int)
:
The process of determining applicability begins by determining the potentially applicable methods (§15.12.2.1). Then, to ensure compatibility with the Java programming language prior to Java SE 5.0, the process continues in three phases:
- The first phase performs overload resolution without permitting boxing or unboxing conversion, or the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the second phase.
注意强调的部分,这意味着编译器将 1
与 int
匹配。由于有一个适用的方法 remove(int)
,因此选择了该方法进行调用。换句话说,首先考虑原始类型,然后再考虑引用类型,例如 Integer
(int
的装箱类型)。
你的主要问题是调用代码。有
l.remove(1);
您正在使用 int
作为通用数据结构的参数,该数据结构是使用 Integer
作为类型参数声明的。这也适用于队列,因为 Java.
如果您使用 Integer
,则两个调用的行为相同:
l.remove(Integer.valueOf(1));
q2.remove(Integer.valueOf(1));
现在这两个调用都删除了所需的 对象。
在你的例子中 l
是类型 List
,它有两个 remove
方法:remove(Object)
继承自 Collection
和 remove(int)
为 List
定义。对 remove(1)
的调用链接到 remove(int)
,因为这是完全匹配。
另一方面,q2
是 Queue
并且此类型未指定 remove(int)
。只有 remove(Object)
来自 Collection
。因此编译器生成从 int
到 Integer
的自动装箱并将调用链接到 remove(Object)
.
可能令人困惑的是:q2 实际上持有 List
。但这没关系。编译器只“看到”引用对象的变量类型。 Object 的真实类型被忽略(在大多数情况下实际上是未知的)。
知道 q2 包含 List
你可以进行类型转换:
((List<Integer>)q2).remove(1);
这将再次导致对 List.remove(int)
的调用,因为编译器随后知道您正在处理的对象是 List