【问题标题】:Why are empty collections of different type equal?为什么不同类型的空集合相等?
【发布时间】:2019-02-11 14:00:59
【问题描述】:

下面是什么机制使不同类型相等?

import static org.testng.Assert.assertEquals;
@Test
public void whyThisIsEqual() {
    assertEquals(new HashSet<>(), new ArrayList<>());
}

【问题讨论】:

    标签: java collections testng


    【解决方案1】:

    他们不是……

    System.out.println(new HashSet<>().equals(new ArrayList<>())); // false
    

    这是特定于testng assertEquals

    查看该方法的文档,它说:

    断言两个集合包含相同顺序的相同元素。

    这对我来说很荒谬,Set 本身没有订单。

    Set<String> set = new HashSet<>();
    set.add("hello");
    set.add("from");
    set.add("jug");
    
    System.out.println(set); // [from, hello, jug]
    
    IntStream.range(0, 1000).mapToObj(x -> x + "").forEachOrdered(set::add);
    IntStream.range(0, 1000).mapToObj(x -> x + "").forEachOrdered(set::remove);
    
    System.out.println(set); // [jug, hello, from]
    

    因此,在某个特定时间点将这些与Collection 进行比较会产生有趣的结果。

    更糟糕的是,java-9Set::of 方法在内部实现了随机化,因此 顺序(或不是顺序)会因运行而异。

    【讨论】:

    • 我发现 junit 调用 assetEquals 但不使用 Object.equals 是一个危险的选择...这会导致像这样的“意外”行为。
    • @AxelH 确实,我真的没想到会这样
    • Junit 在这里抛出一个AssertionError。具有这种行为的是 testng。 (我看到您在我发布此评论时编辑了您的答案)
    • @marstran 看到另一个编辑,我觉得这很奇怪
    • @AxelH & Eugene,我同意,这种方法命名错误。这应该命名为 assertEqualContentsOrdered。如果您知道集合是有序的,那么比较列表和集合会很有用。 (已经有 assertEqualsNoOrder 但它只需要数组。)
    【解决方案2】:

    assertEquals(Collection&lt;?&gt; actual, Collection&lt;?&gt; expected)documentation 说:

    断言两个集合以相同的顺序包含相同的元素。如果没有,则抛出 AssertionError。

    因此,将比较集合的内容,如果两个集合都是空的,则它们是相等的。

    【讨论】:

      【解决方案3】:

      Testng 调用以这种方式实现的方法。

        public static void assertEquals(Collection<?> actual, Collection<?> expected, String message) {
          if (actual == expected) {
            return;
          }
      
          if (actual == null || expected == null) {
            if (message != null) {
              fail(message);
            } else {
              fail("Collections not equal: expected: " + expected + " and actual: " + actual);
            }
          }
      
          assertEquals(
              actual.size(),
              expected.size(),
              (message == null ? "" : message + ": ") + "lists don't have the same size");
      
          Iterator<?> actIt = actual.iterator();
          Iterator<?> expIt = expected.iterator();
          int i = -1;
          while (actIt.hasNext() && expIt.hasNext()) {
            i++;
            Object e = expIt.next();
            Object a = actIt.next();
            String explanation = "Lists differ at element [" + i + "]: " + e + " != " + a;
            String errorMessage = message == null ? explanation : message + ": " + explanation;
            assertEqualsImpl(a, e, errorMessage);
          }
        }
      

      这试图提供帮助,但由于多种原因而效果不佳。

      两个 equals 集合可能看起来不同。

      Set<Integer> a = new HashSet<>();
      a.add(82);
      a.add(100);
      System.err.println(a);
      Set<Integer> b = new HashSet<>();
      for (int i = 82; i <= 100; i++)
          b.add(i);
      for (int i = 83; i <= 99; i++)
          b.remove(i);
      System.err.println(b);
      System.err.println("a.equals(b) && b.equals(a) is " + (a.equals(b) && b.equals(a)));
      assertEquals(a, b, "a <=> b");
      

      Set<Integer> a = new HashSet<>();
      a.add(100);
      a.add(82);
      System.err.println(a);
      Set<Integer> b = new HashSet<>(32);
      b.add(100);
      b.add(82);
      System.err.println(b);
      System.err.println("a.equals(b) && b.equals(a) is " + (a.equals(b) && b.equals(a)));
      assertEquals(a, b, "a <=> b");
      

      打印

      [82, 100]
      [100, 82]
      a.equals(b) && b.equals(a) is true
      Exception in thread "main" java.lang.AssertionError: a <=> b: Lists differ at element [0]: 100 != 82
          at ....
      

      两个集合可以相同也可以不同,具体取决于它们的比较方式。

      assertEquals(a, (Iterable) b); // passes
      
      assertEquals(a, (Object) b); // passes
      
      assertEquals(Arrays.asList(a), Arrays.asList(b)); // passes
      

      【讨论】:

      • @marstran 哎哟,所以引用的类型而不是对象会改变行为。
      • 是的。重载应该有另一个名字恕我直言。比如assertSameElements 什么的。
      • @marstran 或 assertEquals(Object,Object) 应该在运行时检查,但即便如此它真的只对 List not Set 有好处
      • @AxelH 这个函数是不好的其他原因,但它认为null == null 的事实与语言的其余部分完全一致。
      【解决方案4】:

      当我运行下面的代码时,条件是false

      if( (new HashSet<>()).equals(new ArrayList<>())){
                  System.out.println("They are equal");
              }
      

      因此对于assertEquals,它true只检查元素及其顺序是否相等。但对于equals,它是false

      【讨论】:

        【解决方案5】:

        因为集合只比较内容,而不是集合类型。

        其背后的基本原理是,集合的某些子类通常是从测试方法返回的,与使用的子类究竟是什么无关。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-10-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-12-29
          • 2014-11-24
          • 1970-01-01
          相关资源
          最近更新 更多