【问题标题】:Same method invocations, different result in LinkedList, Queue vs. List相同的方法调用,不同的结果在 LinkedList、Queue 和 List
【发布时间】:2021-04-28 15:39:59
【问题描述】:

我已阅读线程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,但只有整数1012

如果我仔细看看接口:

  • Queue 没有remove(int) 方法,但从Collection 继承了remove(Object) 方法。
  • List 有一个 remove(int index)remove(Object o) 方法。

到目前为止,一切都很好。

但是:我不明白,Java 是如何在使用相同语法 (remove(1)) 的同时调用两种不同的方法的?

【问题讨论】:

  • 是的。尽可能避免自动装箱/拆箱。在您的 IDE 中为此启用编译器警告

标签: java list linked-list queue


【解决方案1】:

对于List,编译器根据method signature选择remove(int)

确定适用性的过程从确定可能适用的方法开始(第 15.12.2.1 节)。然后,为确保与 Java SE 5.0 之前的 Java 编程语言兼容,该过程将分三个阶段进行:

  1. 第一阶段执行重载解决不允许装箱或拆箱转换,或使用可变的arity 方法调用。如果在此阶段未找到适用的方法,则处理继续到第二阶段。

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

【讨论】:

    【解决方案2】:

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

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

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

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

    【讨论】:

      【解决方案3】:

      您的主要问题是调用代码。与

      l.remove(1);
      

      您正在使用int 作为通用数据结构的参数,该数据结构以Integer 作为类型参数声明。这也适用于队列,因为 Java 中的自动装箱行为。

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

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

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

      【讨论】:

        【解决方案4】:

        这两种方法的实现是不同的——因为 Queue 只允许从堆栈的底部和顶部移除,所以您必须对这两种移除方法使用特定的方法,而不是用于索引的 List运营商。

        在这种情况下,Queue 覆盖 remove(Object obj) 以删除对象本身,而 list 覆盖 remove(Object obj) 并添加额外的 remove(int index) 以允许删除索引和对象。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-05-15
          • 1970-01-01
          • 1970-01-01
          • 2022-08-05
          相关资源
          最近更新 更多