Java ArrayList - 如何判断两个列表是否相等,顺序无关紧要?

时间:2012-11-21 20:01:37

标签: java arraylist

我有两个ArrayList类型Answer(自制类)。

我想比较两个列表,看看它们是否包含相同的内容,但没有订单问题。

示例:

//These should be equal.
ArrayList<String> listA = {"a", "b", "c"}
ArrayList<String> listB = {"b", "c", "a"}

List.equals表示如果两个列表包含相同的大小,内容和元素顺序,则它们是相等的。我想要同样的事情,但没有订单问题。

有一种简单的方法吗?或者我是否需要执行嵌套for循环,并手动检查两个列表的每个索引?

注意:我无法将它们从ArrayList更改为其他类型的列表,他们需要保留它。

18 个答案:

答案 0 :(得分:126)

对于任何列表,最简单的方法可能是:

listA.containsAll(listB) && listB.containsAll(listA)

答案 1 :(得分:113)

您可以使用Collections.sort()对两个列表进行排序,然后使用equals方法。一个更好的解决方案是首先检查它们在订购前是否长度相同,如果不是,则它们不相等,然后排序,然后使用等于。例如,如果您有两个字符串列表,它将类似于:

public  boolean equalLists(List<String> one, List<String> two){     
    if (one == null && two == null){
        return true;
    }

    if((one == null && two != null) 
      || one != null && two == null
      || one.size() != two.size()){
        return false;
    }

    //to avoid messing the order of the lists we will use a copy
    //as noted in comments by A. R. S.
    one = new ArrayList<String>(one); 
    two = new ArrayList<String>(two);   

    Collections.sort(one);
    Collections.sort(two);      
    return one.equals(two);
}

答案 2 :(得分:72)

Apache Commons 再次收集救援人员:

List<String> listA = Arrays.asList("a", "b", "b", "c");
List<String> listB = Arrays.asList("b", "c", "a", "b");
System.out.println(CollectionUtils.isEqualCollection(listA, listB)); // true

List<String> listC = Arrays.asList("a", "b", "c");
List<String> listD = Arrays.asList("a", "b", "c", "c");
System.out.println(CollectionUtils.isEqualCollection(listC, listD)); // false

文件:

  

org.apache.commons.collections4.CollectionUtils

     

<强>

<强>
public static boolean isEqualCollection(java.util.Collection a,
                                        java.util.Collection b)
     

<强>   如果给定的true包含完全相同的,则返回Collection   具有完全相同基数的元素。

     

也就是说,如果是的话    a e 的基数等于 b e 的基数,    a b 中的元素 e

     

<强>参数:

     
      
  • a - 第一个集合,不得为null
  •   
  • b - 第二个   集合,不得为null
  •   
     

返回: true iff包含   具有相同基数的相同元素。

答案 3 :(得分:9)

// helper class, so we don't have to do a whole lot of autoboxing
private static class Count {
    public int count = 0;
}

public boolean haveSameElements(final List<String> list1, final List<String> list2) {
    // (list1, list1) is always true
    if (list1 == list2) return true;

    // If either list is null, or the lengths are not equal, they can't possibly match 
    if (list1 == null || list2 == null || list1.size() != list2.size())
        return false;

    // (switch the two checks above if (null, null) should return false)

    Map<String, Count> counts = new HashMap<>();

    // Count the items in list1
    for (String item : list1) {
        if (!counts.containsKey(item)) counts.put(item, new Count());
        counts.get(item).count += 1;
    }

    // Subtract the count of items in list2
    for (String item : list2) {
        // If the map doesn't contain the item here, then this item wasn't in list1
        if (!counts.containsKey(item)) return false;
        counts.get(item).count -= 1;
    }

    // If any count is nonzero at this point, then the two lists don't match
    for (Map.Entry<String, Count> entry : counts.entrySet()) {
        if (entry.getValue().count != 0) return false;
    }

    return true;
}

答案 4 :(得分:5)

如果项目的基数没有关系(意思是:重复的元素被认为是一个),那么有一种方法可以做到这一点,而无需排序:

boolean result = new HashSet<>(listA).equals(new HashSet<>(listB));

这将在每个Set中创建List,然后使用HashSet的{​​{1}}方法(当然)无视排序。

如果基数很重要,那么你必须将自己局限于equals提供的设施;在这种情况下,@ jschoen的回答会更合适。

答案 5 :(得分:5)

这是基于@cHao解决方案。我包括了一些修复和性能改进。这相当于等份复制解决方案的两倍。适用于任何集合类型。空集合和null被视为相等。利用你的优势;)

/**
 * Returns if both {@link Collection Collections} contains the same elements, in the same quantities, regardless of order and collection type.
 * <p>
 * Empty collections and {@code null} are regarded as equal.
 */
public static <T> boolean haveSameElements(Collection<T> col1, Collection<T> col2) {
    if (col1 == col2)
        return true;

    // If either list is null, return whether the other is empty
    if (col1 == null)
        return col2.isEmpty();
    if (col2 == null)
        return col1.isEmpty();

    // If lengths are not equal, they can't possibly match
    if (col1.size() != col2.size())
        return false;

    // Helper class, so we don't have to do a whole lot of autoboxing
    class Count
    {
        // Initialize as 1, as we would increment it anyway
        public int count = 1;
    }

    final Map<T, Count> counts = new HashMap<>();

    // Count the items in list1
    for (final T item : col1) {
        final Count count = counts.get(item);
        if (count != null)
            count.count++;
        else
            // If the map doesn't contain the item, put a new count
            counts.put(item, new Count());
    }

    // Subtract the count of items in list2
    for (final T item : col2) {
        final Count count = counts.get(item);
        // If the map doesn't contain the item, or the count is already reduced to 0, the lists are unequal 
        if (count == null || count.count == 0)
            return false;
        count.count--;
    }

    // If any count is nonzero at this point, then the two lists don't match
    for (final Count count : counts.values())
        if (count.count != 0)
            return false;

    return true;
}

答案 6 :(得分:4)

想想你如何在没有计算机或编程语言的情况下自己做这件事。我给你两个元素列表,你必须告诉我它们是否包含相同的元素。你会怎么做?

如上所述,一种方法是对列表进行排序,然后逐个元素地查看它们是否相等(这是List.equals的作用)。这意味着要么你被允许修改列表,要么你被允许复制它们 - 并且在不知道任务的情况下,我不知道是否允许这两个/两者。

另一种方法是遍历每个列表,计算每个元素出现的次数。如果两个列表在末尾具有相同的计数,则它们具有相同的元素。其代码是将每个列表转换为elem -> (# of times the elem appears in the list)的地图,然后在两张地图上调用equals。如果映射是HashMap,则每个转换都是O(N)操作,比较也是如此。这将为您提供一个非常有效的算法,以一些额外的内存为代价。

答案 7 :(得分:4)

我遇到了同样的问题并想出了一个不同的解决方案。当涉及重复时,这个也适用:

public static boolean equalsWithoutOrder(List<?> fst, List<?> snd){
  if(fst != null && snd != null){
    if(fst.size() == snd.size()){
      // create copied lists so the original list is not modified
      List<?> cfst = new ArrayList<Object>(fst);
      List<?> csnd = new ArrayList<Object>(snd);

      Iterator<?> ifst = cfst.iterator();
      boolean foundEqualObject;
      while( ifst.hasNext() ){
        Iterator<?> isnd = csnd.iterator();
        foundEqualObject = false;
        while( isnd.hasNext() ){
          if( ifst.next().equals(isnd.next()) ){
            ifst.remove();
            isnd.remove();
            foundEqualObject = true;
            break;
          }
        }

        if( !foundEqualObject ){
          // fail early
          break;
        }
      }
      if(cfst.isEmpty()){ //both temporary lists have the same size
        return true;
      }
    }
  }else if( fst == null && snd == null ){
    return true;
  }
  return false;
}

与其他一些解决方案相比的优势:

  • 小于O(N²)的复杂度(虽然我没有测试它与其他答案中的解决方案相比的真实性能);
  • 早退;
  • 检查null;
  • 即使涉及重复项也能正常工作:如果您有一个数组[1,2,3,3]和另一个数组[1,2,2,3],这里的大多数解决方案都会告诉您在不考虑订单时它们是相同的。此解决方案通过从临时列表中删除相同的元素来避免这种情况;
  • 使用语义相等(equals)而不是引用相等(==);
  • 不对itens进行排序,因此无需对其进行排序(implement Comparable),以使此解决方案正常工作。

答案 8 :(得分:4)

我说这些答案错过了一个技巧。

Bloch,在他的基本,精彩,简洁的Effective Java中,在第47项中标题和#34;了解和使用图书馆&#34;,&#34;总结一下,不要重新发明轮子&#34;。他给出了几个非常清楚的原因。

这里有一些答案,建议来自Apache Commons Collections库中CollectionUtils的方法,但没有人发现the most beautiful, elegant way of answering this question

Collection<Object> culprits = CollectionUtils.disjunction( list1, list2 );
if( ! culprits.isEmpty() ){
  // ... then in most cases you will ardently wish to do something to these culprits: 
  // at the very least, name-and-shame them.

}

罪犯:即两个Lists都不共有的元素。使用list1list2确定哪些罪犯属于CollectionUtils.intersection( list1, culprits )哪个属于CollectionUtils.intersection( list2, culprits )是相对简单的。
然而,在{&#34; a&#34;,&#34; a&#34;,&#34; b&#34;等情况下,它往往会崩溃。 } disjunction与{&#34; a&#34;,&#34; b&#34;,&#34; b&#34; ......除了这不是软件的失败,而是所期望任务的细微之处/含糊之处所固有的。

注意,我首先感到失望的是,CollectionUtils方法都没有提供重载版本,因此您可以强加自己的Comparator(因此您可以重新定义equals以满足您的目的)。

但是从collections4 4.0开始,有一个新类Equator,其中&#34;确定T&#34;类型的对象之间的相等性。在检查collections4 CollectionUtils.java的源代码时,他们似乎使用了一些方法,但据我所知,这不适用于文件顶部的方法,使用CardinalityHelper class ...包括disjunctionintersection

我猜想阿帕奇人还没有解决这个问题,因为这不是一件轻而易举的事情:你必须创建类似于&#34; AbstractEquatingCollection&#34; class,而不是使用它的元素&#39;固有的equalshashCode方法必须使用Equator的方法来处理所有基本方法,例如addcontains等。事实上,NB当你查看源代码时,AbstractCollection没有实现add,也没有实现它的抽象子类,如AbstractSet ......你必须等到具体的类,如{{1}实现HashSet之前的{}和ArrayList。非常头疼。

我想,同时观看这个空间。显而易见的临时解决方案是将所有元素包装在一个定制的包装器类中,该类使用addequals来实现您想要的那种相等...然后操纵这些包装器的hashCode对象。

答案 9 :(得分:3)

将列表转换为Guava&#39; Multiset非常有效。无论它们的顺序如何,都会进行比较,并且还会考虑重复的元素。

static <T> boolean equalsIgnoreOrder(List<T> a, List<T> b) {
    return ImmutableMultiset.copyOf(a).equals(ImmutableMultiset.copyOf(b));
}

assert equalsIgnoreOrder(ImmutableList.of(3, 1, 2), ImmutableList.of(2, 1, 3));
assert !equalsIgnoreOrder(ImmutableList.of(1), ImmutableList.of(1, 1));

答案 10 :(得分:2)

利用CollectionUtils减法方法的解决方案:

import static org.apache.commons.collections15.CollectionUtils.subtract;

public class CollectionUtils {
  static public <T> boolean equals(Collection<? extends T> a, Collection<? extends T> b) {
    if (a == null && b == null)
      return true;
    if (a == null || b == null || a.size() != b.size())
      return false;
    return subtract(a, b).size() == 0 && subtract(a, b).size() == 0;
  }
}

答案 11 :(得分:2)

如果您不希望对集合进行排序,并且您需要[“A”“B”“C”]不等于[“B”“B”“A”“C”]的结果,< / p>

l1.containsAll(l2)&&l2.containsAll(l1)

还不够,你还需要检查尺寸:

    List<String> l1 =Arrays.asList("A","A","B","C");
    List<String> l2 =Arrays.asList("A","B","C");
    List<String> l3 =Arrays.asList("A","B","C");

    System.out.println(l1.containsAll(l2)&&l2.containsAll(l1));//cautions, this will be true
    System.out.println(isListEqualsWithoutOrder(l1,l2));//false as expected

    System.out.println(l3.containsAll(l2)&&l2.containsAll(l3));//true as expected
    System.out.println(isListEqualsWithoutOrder(l2,l3));//true as expected


    public static boolean isListEqualsWithoutOrder(List<String> l1, List<String> l2) {
        return l1.size()==l2.size() && l1.containsAll(l2)&&l2.containsAll(l1);
}

答案 12 :(得分:1)

如果您关心订单,那么只需使用equals方法:

list1.equals(list2)

如果您不在乎订单,请使用此

Collections.sort(list1);
Collections.sort(list2);      
list1.equals(list2)

答案 13 :(得分:0)

单行方法:)

  1. 集合的项目未实现Comparable接口?超级T>

     static boolean isEqualCollection(Collection<?> a, Collection<?> b) {
         return a == b || (a != null && b != null && a.size() == b.size()
             && a.stream().collect(Collectors.toMap(Function.identity(), s -> 1L, Long::sum)).equals(b.stream().collect(Collectors.toMap(Function.identity(), s -> 1L, Long::sum))));
     }
    
  2. 集合的项目实现接口Comparable <?超级T>

     static <T extends Comparable<? super T>> boolean  isEqualCollection2(Collection<T> a, Collection<T> b) {
       return a == b || (a != null && b != null && a.size() == b.size() && a.stream().sorted().collect(Collectors.toList()).equals(b.stream().sorted().collect(Collectors.toList())));
     }
    
  3. 通过https://github.com/retrostreams/android-retrostreams

    支持Android5和Android6。
    static boolean isEqualCollection(Collection<?> a, Collection<?> b) {
     return a == b || (a != null && b != null && a.size() == b.size()
             && StreamSupport.stream(a).collect(Collectors.toMap(Function.identity(), s->1L, Longs::sum)).equals(StreamSupport.stream(b).collect(Collectors.toMap(Function.identity(), s->1L, Longs::sum))));
    }
    

////测试用例

    boolean isEquals1 = isEqualCollection(null, null); //true
    boolean isEquals2 = isEqualCollection(null, Arrays.asList("1", "2")); //false
    boolean isEquals3 = isEqualCollection(Arrays.asList("1", "2"), null); //false
    boolean isEquals4 = isEqualCollection(Arrays.asList("1", "2", "2"), Arrays.asList("1", "1", "2")); //false
    boolean isEquals5 = isEqualCollection(Arrays.asList("1", "2"), Arrays.asList("2", "1")); //true
    boolean isEquals6 = isEqualCollection(Arrays.asList("1", 2.0), Arrays.asList(2.0, "1")); //true
    boolean isEquals7 = isEqualCollection(Arrays.asList("1", 2.0, 100L), Arrays.asList(2.0, 100L, "1")); //true
    boolean isEquals8 = isEqualCollection(Arrays.asList("1", null, 2.0, 100L), Arrays.asList(2.0, null, 100L, "1")); //true

答案 14 :(得分:0)

我的解决方案。它不是很酷,但是效果很好。

public static boolean isEqualCollection(List<?> a, List<?> b) {

    if (a == null || b == null) {
        throw new NullPointerException("The list a and b must be not null.");
    }

    if (a.size() != b.size()) {
        return false;
    }

    List<?> bCopy = new ArrayList<Object>(b);

    for (int i = 0; i < a.size(); i++) {

        for (int j = 0; j < bCopy.size(); j++) {
            if (a.get(i).equals(bCopy.get(j))) {
                bCopy.remove(j);
                break;
            }
        }
    }

    return bCopy.isEmpty();
}

答案 15 :(得分:0)

这是检查可以包含空值的数组列表的相等性的另一种方法:

List listA = Arrays.asList(null, "b", "c");
List listB = Arrays.asList("b", "c", null);

System.out.println(checkEquality(listA, listB)); // will return TRUE


private List<String> getSortedArrayList(List<String> arrayList)
{
    String[] array = arrayList.toArray(new String[arrayList.size()]);

    Arrays.sort(array, new Comparator<String>()
    {
        @Override
        public int compare(String o1, String o2)
        {
            if (o1 == null && o2 == null)
            {
                return 0;
            }
            if (o1 == null)
            {
                return 1;
            }
            if (o2 == null)
            {
                return -1;
            }
            return o1.compareTo(o2);
        }
    });

    return new ArrayList(Arrays.asList(array));
}

private Boolean checkEquality(List<String> listA, List<String> listB)
{
    listA = getSortedArrayList(listA);
    listB = getSortedArrayList(listB);

    String[] arrayA = listA.toArray(new String[listA.size()]);
    String[] arrayB = listB.toArray(new String[listB.size()]);

    return Arrays.deepEquals(arrayA, arrayB);
}

答案 16 :(得分:0)

两全其美[@DiddiZ,@ Chalkos]:这个主要基于@Chalkos方法构建,但修复了一个错误(ifst.next()),并改进了初始检查(取自@DiddiZ)以及删除需要复制第一个集合(只是从第二个集合的副本中删除项目)。

不需要散列函数或排序,并且在不相等时启用早期存在,这是最有效的实现。除非你有数千或更多的集合长度,并且有一个非常简单的散列函数。

public static <T> boolean isCollectionMatch(Collection<T> one, Collection<T> two) {
    if (one == two)
        return true;

    // If either list is null, return whether the other is empty
    if (one == null)
        return two.isEmpty();
    if (two == null)
        return one.isEmpty();

    // If lengths are not equal, they can't possibly match
    if (one.size() != two.size())
        return false;

    // copy the second list, so it can be modified
    final List<T> ctwo = new ArrayList<>(two);

    for (T itm : one) {
        Iterator<T> it = ctwo.iterator();
        boolean gotEq = false;
        while (it.hasNext()) {
            if (itm.equals(it.next())) {
                it.remove();
                gotEq = true;
                break;
            }
        }
        if (!gotEq) return false;
    }
    // All elements in one were found in two, and they're the same size.
    return true;
}

答案 17 :(得分:-1)

在这种情况下,列表{“a”,“b”}和{“b”,“a”}相等。并且{“a”,“b”}和{“b”,“a”,“c”}不相等。如果您使用复杂对象列表,请记得覆盖 equals 方法,因为 containsAll 在里面使用它。

if (oneList.size() == secondList.size() && oneList.containsAll(secondList)){
        areEqual = true;
}