相同的方法调用,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]

我知道,为什么输出不同:

如果我仔细看看接口:

到目前为止,还不错。

但是:我不明白,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:

  1. 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.

注意强调的部分,这意味着编译器将 1int 匹配。由于有一个适用的方法 remove(int),因此选择了该方法进行调用。换句话说,首先考虑原始类型,然后再考虑引用类型,例如 Integerint 的装箱类型)。

你的主要问题是调用代码。有

l.remove(1);

您正在使用 int 作为通用数据结构的参数,该数据结构是使用 Integer 作为类型参数声明的。这也适用于队列,因为 Java.

中的自动装箱行为

如果您使用 Integer,则两个调用的行为相同:

l.remove(Integer.valueOf(1));
q2.remove(Integer.valueOf(1));

现在这两个调用都删除了所需的 对象

在你的例子中 l 是类型 List,它有两个 remove 方法:remove(Object) 继承自 Collectionremove(int)List 定义。对 remove(1) 的调用链接到 remove(int),因为这是完全匹配。 另一方面,q2Queue 并且此类型未指定 remove(int)。只有 remove(Object) 来自 Collection。因此编译器生成从 intInteger 的自动装箱并将调用链接到 remove(Object).

可能令人困惑的是:q2 实际上持有 List。但这没关系。编译器只“看到”引用对象的变量类型。 Object 的真实类型被忽略(在大多数情况下实际上是未知的)。 知道 q2 包含 List 你可以进行类型转换:

((List<Integer>)q2).remove(1);

这将再次导致对 List.remove(int) 的调用,因为编译器随后知道您正在处理的对象是 List