【问题标题】:Properly removing an Integer from a List<Integer>从 List<Integer> 中正确删除整数
【发布时间】:2017-12-29 13:15:01
【问题描述】:

这是我刚刚遇到的一个很好的陷阱。 考虑一个整数列表:

List<Integer> list = new ArrayList<Integer>();
list.add(5);
list.add(6);
list.add(7);
list.add(1);

对执行list.remove(1) 时会发生什么有任何有根据的猜测吗? list.remove(new Integer(1)) 呢?这可能会导致一些讨厌的错误。

在处理整数列表时,区分remove(int index)(从给定索引中删除一个元素)和remove(Object o)(通过引用删除一个元素)的正确方法是什么?


这里要考虑的要点是@Nikita mentioned - 精确的参数匹配优先于自动装箱。

【问题讨论】:

  • 答:这里真正的问题是 Sun 的某个人不知何故认为围绕原语拥有(不可变的)包装类很聪明,后来有人认为拥有自动(取消)装箱更聪明...... 当存在更好的 API 时,人们会继续使用蹩脚的默认 API。出于很多目的,有比 new Arraylist更好 的解决方案。例如 Trove 提供了一个 TIntArrayList。我用 Java 编程的次数越多(SCJP 自 2001 年以来),我使用包装类的次数就越少,我使用设计良好的 API 的次数就越多(想到 Trove、Google 等)。

标签: java collections overloading


【解决方案1】:

Java 总是调用最适合您的论点的方法。自动装箱和隐式向上转换仅在没有可以在没有强制转换/自动装箱的情况下调用的方法时执行。

List接口指定了两个remove方法(请注意参数的命名):

  • remove(Object o)
  • remove(int index)

这意味着list.remove(1) 会删除位置 1 的对象,remove(new Integer(1)) 会从此列表中删除指定元素的第一个匹配项。

【讨论】:

  • 挑选一个 nit:Integer.valueOf(1)new Integer(1) 更好。静态方法可以做缓存等,所以你会得到更好的性能。
  • Peter Lawrey 的建议更好,避免了不必要的对象创建。
  • @assylias:Peter Lawrey 的提案与 decitrig 的提案完全相同,只是透明度较低。
  • @MarkPeters 我的评论是关于new Integer(1),但我同意Integer.valueOf(1)(Integer) 1 是等价的。
【解决方案2】:

你可以使用强制转换

list.remove((int) n);

list.remove((Integer) n);

n 是 int 还是 Integer 无关紧要,该方法将始终调用您期望的方法。

使用(Integer) nInteger.valueOf(n)new Integer(n) 更有效,因为前两个可以使用整数缓存,而后一个将始终创建一个对象。

【讨论】:

  • 如果你能解释为什么会这样就好了:) [自动装箱条件...]
  • 通过使用强制转换,您可以确保编译器看到您期望的类型。在第一种情况下,'(int) n' 只能是 int 类型,在第二种情况下,'(Integer) n' 只能是 Integer 类型。 'n' 将根据需要进行转换/装箱/拆箱,如果不能,您将收到编译器错误。
【解决方案3】:

我不知道“正确”的方式,但你建议的方式工作得很好:

list.remove(int_parameter);

删除给定位置的元素并

list.remove(Integer_parameter);

从列表中删除给定的对象。

这是因为 VM 一开始试图找到用完全相同相同参数类型声明的方法,然后才尝试自动装箱。

【讨论】:

    【解决方案4】:

    list.remove(4)list.remove(int index) 完全匹配,因此会被调用。如果您想致电list.remove(Object),请执行以下操作:list.remove((Integer)4)

    【讨论】:

    • 谢谢Petar,像你上面写的那样简单的(Integer)演员似乎对我来说是最简单的方法。
    • 使用最后一种方法时,它似乎返回一个布尔值。尝试堆叠多个删除时,我收到无法在布尔值上调用 remove 的错误。
    【解决方案5】:

    对执行 list.remove(1) 时会发生什么有任何有根据的猜测吗? list.remove(new Integer(1)) 呢?

    无需猜测。第一种情况会导致List.remove(int)被调用,而1位置的元素将被移除。第二种情况会导致List.remove(Integer)被调用,值等于Integer(1)的元素会被移除。在这两种情况下,Java 编译器都会选择最接近的匹配重载。

    是的,这里可能会造成混淆(和错误),但这是一个相当少见的用例。

    当在 Java 1.2 中定义了两个 List.remove 方法时,重载并没有歧义。该问题仅在 Java 1.5 中引入泛型和自动装箱时出现。事后看来,如果给删除方法之一赋予不同的名称会更好。但现在为时已晚。

    【讨论】:

      【解决方案6】:

      请注意,即使 VM 没有做正确的事情(它确实做了),您仍然可以通过使用 remove(java.lang.Object) 对任意对象进行操作这一事实来确保正确的行为:

      myList.remove(new Object() {
        @Override
        public boolean equals(Object other) {
          int k = ((Integer) other).intValue();
          return k == 1;
        }
      }
      

      【讨论】:

      • 这个“解决方案”打破了 equals 方法的约定,特别是(来自 Javadoc)“它是对称的:对于任何非空引用值 x 和 y,x.equals(y)当且仅当 y.equals(x) 返回 true 时才应返回 true。"。因此,不能保证在 List 的所有实现上都有效,因为 List 的任何实现都可以随意交换 x.equals(y) 中的 x 和 y,因为 Object.equals 的 Javadoc 说这应该是有效的.
      【解决方案7】:

      我确实喜欢按照#decitrig 在接受的答案第一评论中的建议。

      list.remove(Integer.valueOf(intereger_parameter));
      

      这对我有帮助。再次感谢#decitrig 的评论。它可能对某些人有所帮助。

      【讨论】:

        【解决方案8】:

        这就是诀窍。

        我们这里举两个例子:

        public class ArrayListExample {
        
        public static void main(String[] args) {
            Collection<Integer> collection = new ArrayList<>();
            List<Integer> arrayList = new ArrayList<>();
        
            collection.add(1);
            collection.add(2);
            collection.add(3);
            collection.add(null);
            collection.add(4);
            collection.add(null);
            System.out.println("Collection" + collection);
        
            arrayList.add(1);
            arrayList.add(2);
            arrayList.add(3);
            arrayList.add(null);
            arrayList.add(4);
            arrayList.add(null);
            System.out.println("ArrayList" + arrayList);
        
            collection.remove(3);
            arrayList.remove(3);
            System.out.println("");
            System.out.println("After Removal of '3' :");
            System.out.println("Collection" + collection);
            System.out.println("ArrayList" + arrayList);
        
            collection.remove(null);
            arrayList.remove(null);
            System.out.println("");
            System.out.println("After Removal of 'null': ");
            System.out.println("Collection" + collection);
            System.out.println("ArrayList" + arrayList);
        
          }
        
        }
        

        现在让我们看看输出:

        Collection[1, 2, 3, null, 4, null]
        ArrayList[1, 2, 3, null, 4, null]
        
        After Removal of '3' :
        Collection[1, 2, null, 4, null]
        ArrayList[1, 2, 3, 4, null]
        
        After Removal of 'null': 
        Collection[1, 2, 4, null]
        ArrayList[1, 2, 3, 4]
        

        现在让我们分析一下输出:

        1. 当从集合中删除 3 时,它会调用集合的 remove() 方法,该方法以 Object o 作为参数。因此它删除了对象3。 但在 arrayList 对象中,它被索引 3 覆盖,因此第 4 个元素被删除。

        2. 通过对象移除的相同逻辑,在第二个输出中的两种情况下都会移除空值。

        所以要删除数字 3 这是一个对象,我们需要明确地将 3 作为 object 传递。

        这可以通过使用包装类Integer进行强制转换或包装来完成。

        例如:

        Integer removeIndex = Integer.valueOf("3");
        collection.remove(removeIndex);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-11-15
          • 1970-01-01
          • 2020-02-25
          相关资源
          最近更新 更多