Java ArrayList – 如何判断两个列表是否相等,顺序无关紧要?
我有两个答案(自制类)的ArrayList。
我想比较两个列表,看他们是否包含相同的内容,但没有订单的问题。
例:
//These should be equal. ArrayList<String> listA = {"a", "b", "c"} ArrayList<String> listB = {"b", "c", "a"}
List.equals指出,如果两个列表包含相同的大小,内容和元素顺序,则它们是相等的。 我想要同样的事情,但是没有命令。
有一个简单的方法来做到这一点? 或者我将需要做一个嵌套的循环,并手动检查两个列表的每个索引?
注:我不能将它们从ArrayList更改为另一种types的列表,他们需要保持这一点。
您可以使用Collections.sort()
对两个列表进行sorting,然后使用equals方法。 稍微好一点的解决办法是在订货前先检查它们的长度是否相同,如果不一致,则不相等,然后进行sorting,然后使用等号。 例如,如果你有两个string的列表,它将是这样的:
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 ARS one = new ArrayList<String>(one); two = new ArrayList<String>(two); Collections.sort(one); Collections.sort(two); return one.equals(two); }
对任何列表来说,最简单的方法可能是:
listA.containsAll(listB) && listB.containsAll(listA)
Apache Commons Collections再次拯救:
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)
如果给定的
Collection
包含完全相同的元素,并且具有完全相同的基数,则返回true
。也就是说,如果a中的e的基数等于b中的e的基数,那么对于a或b中的每个元素e 。
参数:
a
– 第一个集合,不能为null
b
– 第二个集合,不能为null
返回: 如果集合包含具有相同基数的相同元素,则 返回
true
。
// 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; }
想一想,如果没有计算机或编程语言,你会如何做到这一点。 我给你两个元素列表,你必须告诉我它们是否包含相同的元素。 你会怎么做?
如上所述,一种方法是对列表进行sorting,然后逐个查看它们是否相等(这是List.equals
作用)。 这意味着要么你被允许修改名单,要么你被允许复制他们 – 而且不知道这个任务,我不知道是否允许任何一个/两个。
另一种方法是查看每个列表,计算每个元素出现的次数。 如果两个列表最后都有相同的计数,则它们具有相同的元素。 代码是将每个列表翻译成elem -> (# of times the elem appears in the list)
的映射,然后在两个地图上调用equals
。 如果地图是HashMap
,那么这些翻译中的每一个都是O(N)操作,就像比较一样。 这将给你一个非常有效的algorithm,在时间上,以一些额外的内存为代价。
我会说这些答案错过了一个把戏。
布洛赫在他的基本的,精彩的,简洁的有效的Java中说,在第47项标题“知道和使用图书馆”,“总结,不要重新发明轮子”。 他给出了几个非常明确的原因为什么不。
这里有几个答案,它们提供了Apache Commons Collections库中CollectionUtils
方法,但没有一个人发现了回答这个问题的最美丽,最优雅的方法 :
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
不共同的要素。 通过使用CollectionUtils.intersection( list1, culprits )
和CollectionUtils.intersection( list2, culprits )
来确定哪些罪犯属于list1
,哪些是list2
是相对直接的。
但是,在{“a”,“a”,“b”}与{“a”,“b”,“b”}分离的情况下,它往往会分崩离析,除非这不是软件的失败,但是与所期望的任务的微妙/模糊性质有关。
NB我首先感到失望的是,没有一个CollectionUtils
方法提供了一个重载的版本,使您可以强加自己的Comparator
(因此您可以重新定义equals
适合您的目的)。
但是从集合4.0还有一个新的类Equator
,它可以“确定typesT的对象之间的等式”。 在检查集合的源代码CollectionUtils.java他们似乎用一些方法,但据我所知,这不适用于该文件的顶部方法,使用CardinalityHelper
类…其中包括disjunction
和intersection
。
我猜测,Apache的人还没有到这个地步,因为它是不平凡的:你将不得不创build类似于“AbstractEquatingCollection”类,而不是使用其元素固有的equals
和hashCode
方法将不得不使用Equator
的所有基本方法,比如add
, contains
等等。NB实际上当你看源代码的时候, AbstractCollection
并没有实现add
,也没有做其抽象的子类,比如AbstractSet
…你必须等待直到add
之前的具体类如HashSet
和ArrayList
被实现。 很头痛。
同时观看这个空间,我想。 显而易见的临时解决scheme是将所有元素包装在一个定制的包装类中,该类使用equals
和hashCode
来实现所需的相等性…然后处理这些包装对象的集合。
这是基于@cHao解决scheme。 我包含了一些修复和性能改进。 这运行的速度大约是平等订购复印解决scheme的两倍。 适用于任何集合types。 空集合和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; }
将列表转换成番石榴的Multiset工作得很好。 不pipe它们的顺序如何,都会对它们进行比较,并且也会考虑到重复的元素。
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));
如果项目的基数不重要(意思是:重复的元素被视为一个),那么有一种方法可以做到这一点,而无需sorting:
boolean result = new HashSet(listA).equals(new HashSet(listB));
这将创build一个列出每个List
,然后使用HashSet
的equals
方法(当然)无视sorting。
如果基数很重要,那么您必须将自己限制在List
提供的设施中; @ jschoen的答案在这种情况下更合适。
我有这个相同的问题,并提出了一个不同的解决scheme。 这个也适用于涉及重复的情况:
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; }
与其他一些解决scheme相比的优势:
- 小于O(N 2)的复杂性(尽pipe我还没有testing过与其他答案中的解决scheme相比的真实性能)。
- 早退;
- 检查为空;
- 即使在涉及到重复项时也是如此:如果您有一个数组
[1,2,3,3]
和另一个数组[1,2,2,3]
大多数解决scheme都会告诉您在不考虑顺序时它们是相同的。 该解决scheme通过从临时列表中删除相同的元素来避免这种情况; - 使用语义相等(
equals
)而不是引用相等(==
); - 不sortingitens,所以他们不需要sorting(通过
implement Comparable
)这个解决scheme的工作。
利用CollectionUtils减法的解决scheme:
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; } }
如果您不希望对收集进行sorting,而您需要[“A”,“B”,“C”]不等于[“B”,“B”,“A”,“C”]的结果,
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); }
最好的两个世界[@DiddiZ,@Chalkos]:这一个主要build立在@Chalkos方法上,但修复了一个错误(ifst.next()),并改进了初始检查(从@DiddiZ中取得),同时也不需要复制第一个集合(仅删除第二个集合的副本中的项目)。
不需要哈希函数或sorting,并且能够早日实现不平等,这是最有效的实现。 这是除非你有一个在数千或更多的收集长度,和一个非常简单的散列函数。
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; }
这是检查可以包含空值的数组列表的相等性的一种替代方法:
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); }
如果你关心订单,那么只需使用equals方法:
list1.equals(list2)
在这种情况下,列表{“a”,“b”}和{“b”,“a”}是相等的。 而{“a”,“b”}和{“b”,“a”,“c”}不相等。 如果使用复杂对象列表,请记住重写equals方法,因为containsAll在里面使用它。
if (oneList.size() == secondList.size() && oneList.containsAll(secondList)){ areEqual = true; }