在标准Java库中查找两个列表是否包含完全相同的元素的最简单方法是什么?
两个列表是否是同一个实例并不重要,如果列表的类型参数不同则无关紧要。
e.g。
List list1
List<String> list2;
// ... construct etc
list1.add("A");
list2.add("A");
// the function, given these two lists, should return true
我知道的脸上可能有什么东西让我盯着: - )
编辑:为了澄清,我正在按顺序寻找完全相同的元素和元素数量。
答案 0 :(得分:309)
如果您关心订单,那么只需使用equals方法:
list1.equals(list2)
来自javadoc:
比较指定的对象 这个平等的清单。返回true 当且仅当指定的对象是 也是一个列表,两个列表都相同 大小,以及所有相应的对 两个列表中的元素是相等的。 (如果,两个元素e1和e2相等 (e1 == null?e2 == null: e1.equals(e2))。)换句话说,两个 如果列表被定义为相等 包含相同的元素 订购。这个定义确保了 equals方法正常工作 跨越不同的实现 列表界面。
如果要独立于顺序进行检查,可以将所有元素复制到集合,并在生成的集合上使用等于:
public static <T> boolean listEqualsIgnoreOrder(List<T> list1, List<T> list2) {
return new HashSet<>(list1).equals(new HashSet<>(list2));
}
这种方法的局限在于它不仅忽略了顺序,而且忽略了重复元素的频率。例如,如果list1
是[“A”,“B”,“A”]而list2
是[“A”,“B”,“B”]那么Set
方法会认为它们是平等的。
如果您需要对订单不敏感但对重复频率敏感,您可以:
答案 1 :(得分:82)
我在评论中发布了一些内容,我认为它保证自己的答案。
正如大家所说,使用equals()取决于顺序。如果您不关心订单,您有3个选择。
选项1
使用containsAll()
。在我看来,这个选项并不理想,因为它提供了最差的性能,O(n ^ 2)。
选项2
这有两种变体:
2a)如果您不关心维护列表的顺序,请在两个列表中使用Collections.sort()
。然后使用equals()
。这是O(nlogn),因为你做了两种排序,然后是O(n)比较。
2b)如果您需要维护列表的顺序,则可以先复制两个列表。然后,您可以在复制的列表上使用解决方案 2a 。但是,如果复制非常昂贵,这可能没有吸引力。
这导致:
选项3
如果您的要求与 2b 部分相同,但复制过于昂贵。您可以使用TreeSet为您进行排序。将每个列表转储到自己的TreeSet中。它将在集合中排序,原始列表将保持不变。然后对equals()
个TreeSet
进行TreeSets
比较。 equals()
可以在O(nlogn)时间内构建,{{1}}是O(n)。
选择: - )。
编辑: 我几乎忘记了Laurence Gonsalves指出的同一个警告。 TreeSet实现将消除重复。如果您关心重复项,则需要某种排序的多重集。
答案 2 :(得分:16)
如果你正在使用(或者很乐意使用)Apache Commons Collections,你可以使用CollectionUtils.isEqualCollection,如果给定的集合包含完全相同的基数,则返回true。“
答案 3 :(得分:8)
聚会很晚但想要添加这个空的安全检查:
Objects.equals(list1, list2)
答案 4 :(得分:6)
我知道这是一个旧线程,但其他答案都没有完全解决我的用例(我猜Guava Multiset可能也会这样做,但这里没有例子)。请原谅我的格式。我仍然很想在堆栈交换上发帖。另外,如果有任何错误,请告诉我
假设您有List<T>
a和List<T>
b,并且您想检查它们是否与以下条件相同:
1)O(n)预计运行时间
2)等式定义为:对于a或b中的所有元素,元素在a中出现的次数等于元素在b中出现的次数。元素相等定义为T.equals()
private boolean listsAreEquivelent(List<? extends Object> a, List<? extends Object> b) {
if(a==null) {
if(b==null) {
//Here 2 null lists are equivelent. You may want to change this.
return true;
} else {
return false;
}
}
if(b==null) {
return false;
}
Map<Object, Integer> tempMap = new HashMap<>();
for(Object element : a) {
Integer currentCount = tempMap.get(element);
if(currentCount == null) {
tempMap.put(element, 1);
} else {
tempMap.put(element, currentCount+1);
}
}
for(Object element : b) {
Integer currentCount = tempMap.get(element);
if(currentCount == null) {
return false;
} else {
tempMap.put(element, currentCount-1);
}
}
for(Integer count : tempMap.values()) {
if(count != 0) {
return false;
}
}
return true;
}
运行时间是O(n),因为我们正在对散列映射进行O(2 * n)插入,并且O(3 * n)散列映射选择。我还没有完全测试这段代码,所以要注意:)
//Returns true:
listsAreEquivelent(Arrays.asList("A","A","B"),Arrays.asList("B","A","A"));
listsAreEquivelent(null,null);
//Returns false:
listsAreEquivelent(Arrays.asList("A","A","B"),Arrays.asList("B","A","B"));
listsAreEquivelent(Arrays.asList("A","A","B"),Arrays.asList("A","B"));
listsAreEquivelent(Arrays.asList("A","A","B"),null);
答案 5 :(得分:4)
尝试此版本不需要相同的顺序,但支持具有多个相同的值。它们仅在每个具有相同数量的任何值时匹配。
public boolean arraysMatch(List<String> elements1, List<String> elements2) {
// Optional quick test since size must match
if (elements1.size() != elements2.size()) {
return false;
}
List<String> work = newArrayList(elements2);
for (String element : elements1) {
if (!work.remove(element)) {
return false;
}
}
return work.isEmpty();
}
答案 6 :(得分:2)
汤姆的回答非常好我完全同意他的回答!
这个问题的一个有趣的方面是,你是否需要List
类型本身及其固有的顺序。
如果不是,您可以降级为Iterable
或Collection
,这样可以灵活地传递按插入时间排序的数据结构,而不是在您要检查的时间。
如果订单永远不重要(并且您没有重复的元素),请考虑使用Set
。
如果订单很重要但是由插入时间定义(并且您没有重复项),请考虑LinkedHashSet
,它类似于TreeSet,但按插入时间排序(不计算重复项)。这也为您O(1)
提供了O(log n)
分期付款访问权限。
答案 7 :(得分:2)
两个列表具有相同元素但顺序不同的情况的解决方案:
public boolean isDifferentLists(List<Integer> listOne, List<Integer> listTwo) {
if(isNullLists(listOne, listTwo)) {
return false;
}
if (hasDifferentSize(listOne, listTwo)) {
return true;
}
List<Integer> listOneCopy = Lists.newArrayList(listOne);
List<Integer> listTwoCopy = Lists.newArrayList(listTwo);
listOneCopy.removeAll(listTwoCopy);
return CollectionUtils.isNotEmpty(listOneCopy);
}
private boolean isNullLists(List<Integer> listOne, List<Integer> listTwo) {
return listOne == null && listTwo == null;
}
private boolean hasDifferentSize(List<Integer> listOne, List<Integer> listTwo) {
return (listOne == null && listTwo != null) || (listOne != null && listTwo == null) || (listOne.size() != listTwo.size());
}
答案 8 :(得分:1)
List上的equals方法将执行此操作,列表是有序的,因此要等于两个列表必须具有相同顺序的相同元素。
return list1.equals(list2);
答案 9 :(得分:1)
除了劳伦斯的回答,如果你还想让它安全无效:
private static <T> boolean listEqualsIgnoreOrder(List<T> list1, List<T> list2) {
if (list1 == null)
return list2==null;
if (list2 == null)
return list1 == null;
return new HashSet<>(list1).equals(new HashSet<>(list2));
}
答案 10 :(得分:1)
示例代码:
public static '<'T'>' boolean isListDifferent(List'<'T'>' previousList,
List'<'T'>' newList) {
int sizePrevoisList = -1;
int sizeNewList = -1;
if (previousList != null && !previousList.isEmpty()) {
sizePrevoisList = previousList.size();
}
if (newList != null && !newList.isEmpty()) {
sizeNewList = newList.size();
}
if ((sizePrevoisList == -1) && (sizeNewList == -1)) {
return false;
}
if (sizeNewList != sizePrevoisList) {
return true;
}
List n_prevois = new ArrayList(previousList);
List n_new = new ArrayList(newList);
try {
Collections.sort(n_prevois);
Collections.sort(n_new);
} catch (ClassCastException exp) {
return true;
}
for (int i = 0; i < sizeNewList; i++) {
Object obj_prevois = n_prevois.get(i);
Object obj_new = n_new.get(i);
if (obj_new.equals(obj_prevois)) {
// Object are same
} else {
return true;
}
}
return false;
}
答案 11 :(得分:1)
list1.equals(list2);
如果您的列表包含自定义类MyClass,则此类必须覆盖equals
函数。
class MyClass
{
int field=0;
@0verride
public boolean equals(Object other)
{
if(this==other) return true;
if(other==null || !(other instanceof MyClass)) return false;
return this.field== MyClass.class.cast(other).field;
}
}
注意:如果要在java.util.Set而不是java.util.List
上测试等于,那么您的对象必须覆盖hashCode
函数。
答案 12 :(得分:0)
您可以使用Apache的org.apache.commons.collections库: http://commons.apache.org/collections/apidocs/org/apache/commons/collections/ListUtils.html
public static boolean isEqualList(java.util.Collection list1,
java.util.Collection list2)
答案 13 :(得分:0)
检查两个列表都不为空。如果它们的大小不同,则这些列表不相等。构建由列表组成的地图&#39;元素作为键及其重复值作为值并比较地图。
假设,如果两个列表都为空,我认为它们是相等的。
private boolean compareLists(List<?> l1, List<?> l2) {
if (l1 == null && l2 == null) {
return true;
} else if (l1 == null || l2 == null) {
return false;
}
if (l1.size() != l2.size()) {
return false;
}
Map<?, Integer> m1 = toMap(l1);
Map<?, Integer> m2 = toMap(l2);
return m1.equals(m2);
}
private Map<Object, Integer> toMap(List<?> list) {
//Effective size, not to resize in the future.
int mapSize = (int) (list.size() / 0.75 + 1);
Map<Object, Integer> map = new HashMap<>(mapSize);
for (Object o : list) {
Integer count = map.get(o);
if (count == null) {
map.put(o, 1);
} else {
map.put(o, ++count);
}
}
System.out.println(map);
return map;
}
请注意,应该为这些对象正确定义方法等于。 https://stackoverflow.com/a/24814634/4587961
答案 14 :(得分:0)
答案 15 :(得分:0)
我的解决方案适用于您不关心 Lists
内的排序的情况 - 换句话说:Lists
具有相同的元素但不同的排序将被视为具有相同的内容.
示例:["word1", "word2"]
和 ["word2", "word1"]
被认为具有相同的内容。
我已经解决了订购问题,我还需要说一下重复项。 Lists
需要具有相同数量的元素才能被视为相等。
示例:["word1"]
和 ["word1", "word1"]
被认为具有不同的内容。
我的解决方案:
public class ListUtil {
public static <T> boolean hasSameContents(List<T> firstList, List<T> secondList) {
if (firstList == secondList) { // same object
return true;
}
if (firstList != null && secondList != null) {
if (firstList.isEmpty() && secondList.isEmpty()) {
return true;
}
if (firstList.size() != secondList.size()) {
return false;
}
List<T> tmpSecondList = new ArrayList<>(secondList);
Object currFirstObject = null;
for (int i=1 ; i<=firstList.size() ; i++) {
currFirstObject = firstList.get(i-1);
boolean removed = tmpSecondList.remove(currFirstObject);
if (!removed) {
return false;
}
if (i != firstList.size()) { // Not the last element
if (tmpSecondList.isEmpty()) {
return false;
}
}
}
if (tmpSecondList.isEmpty()) {
return true;
}
}
return false;
}
}
我已经使用 Strings
对其进行了如下测试:
@Test
public void testHasSameContents() throws Exception {
// comparing with same list => no duplicate elements
Assert.isTrue(ListUtil.hasSameContents(List.of("one", "two", "three"), List.of("one", "two", "three")));
// comparing with same list => duplicate elements
Assert.isTrue(ListUtil.hasSameContents(List.of("one", "two", "three", "one"), List.of("one", "two", "three", "one")));
// compare with disordered list => no duplicate elements
Assert.isTrue(ListUtil.hasSameContents(List.of("one", "two", "three"), List.of("three", "two", "one")));
// compare with disordered list => duplicate elements
Assert.isTrue(ListUtil.hasSameContents(List.of("one", "two", "three", "one"), List.of("three", "two", "one", "one")));
// comparing with different list => same size, no duplicate elements
Assert.isFalse(ListUtil.hasSameContents(List.of("one", "two", "three"), List.of("four", "five", "six")));
// comparing with different list => same size, duplicate elements
Assert.isFalse(ListUtil.hasSameContents(List.of("one", "two", "two"), List.of("one", "two", "three")));
Assert.isFalse(ListUtil.hasSameContents(List.of("one", "two", "three"), List.of("one", "two", "two")));
// comparing with different list => different size, no duplicate elements
Assert.isFalse(ListUtil.hasSameContents(List.of("one", "two", "three", "four"), List.of("one", "two", "three")));
Assert.isFalse(ListUtil.hasSameContents(List.of("one", "two", "three"), List.of("one", "two", "three", "four")));
// comparing with different list => different sizes, duplicate elements
Assert.isFalse(ListUtil.hasSameContents(List.of("one", "two", "three", "one"), List.of("one", "two", "three")));
Assert.isFalse(ListUtil.hasSameContents(List.of("one", "two", "three"), List.of("one", "two", "three", "one")));
}
答案 16 :(得分:0)
我知道这可能会非常晚,但我个人使用此功能。 如果有人想做一些基准测试,那就太好了。
public static<X> boolean areEqual(List<X> a, List<X> b, BiPredicate<X, X> AEqualsB) {
boolean aIsNull = a == null;
boolean bIsNull = b == null;
if (aIsNull || bIsNull) {
return aIsNull == bIsNull;
}
int size = a.size();
boolean sameSize = size == b.size();
if (!sameSize) {return false;} else {
for (int i = 0; i < size; i++) {
X aX = a.get(i), bX = b.get(i);
boolean areEqual = AEqualsB.test(aX, bX);
if (!areEqual) {
return false;
}
}
return true;
}
}
顺便说一句,我知道前 5 行可以用 XOR "^" 加一个 else 来简化,但不管你信不信,我很难得到正确的 XOR。
我猜它的效率将取决于谓词的类型,但同时它允许您检查自定义的潜在等式,而忽略对编码人员来说可能无关紧要的差异。
这是代码示例。
ListUtils.areEqual(newElements, oldElements, Element::areEqual)
public boolean areEqual(Element e) {
return optionalAdapterId() == e.optionalAdapterId()
&& value == e.value
&& valueTotal == e.valueTotal
&& stockTotal == e.stockTotal
&& element_title.equals(e.element_title);
}
至于效率如何,我认为任何迭代总是很昂贵的,这就是为什么每当我需要对大列表使用这个函数时,我都会在一个单独的线程上执行它的操作,并在一个线程上检索响应这需要它,即使知道在哪一点会很好,在不同的线程上执行它是否有益,需要这种线程的项目数量是多少,该信息将添加到文档中。< /p>
答案 17 :(得分:0)
这是一种比较两个考虑重复的集合的方法。因此,集合大小不一定相同。因此,它将在“实际”中查找“预期”:
private static <T> boolean containsAllExpected(Collection<T> actual, Collection<T> expected) {
if (actual == null && expected == null) {
return true;
}
if (actual == null || expected == null) {
return false;
}
Collection<T> a = new ArrayList<>(actual);
Collection<T> e = new ArrayList<>(expected);
Iterator<T> ei = e.iterator();
while (ei.hasNext()) {
T item = ei.next();
if (a.contains(item)) {
ei.remove();
a.remove(item);
} else {
return false;
}
}
return true;
}
享受:)
答案 18 :(得分:-2)
这取决于您使用的具体List类。抽象类AbstractCollection有一个名为containsAll(Collection)的方法,它接受另一个集合(List是一个集合)和:
如果此collection包含指定collection中的所有元素,则返回true。
因此,如果传入ArrayList,您可以调用此方法以查看它们是否完全相同。
List foo = new ArrayList();
List bar = new ArrayList();
String str = "foobar";
foo.add(str);
bar.add(str);
foo.containsAll(bar);
containsAll()的原因是因为它遍历第一个列表,在第二个列表中查找匹配项。因此,如果它们出现故障,则equals()不会接收它。
编辑: 我只想在这里评论一下执行所提供的各种选项的摊销运行时间。运行时间重要吗?当然。这是你唯一应该考虑的事情吗?否。
将每个单个元素从列表复制到其他列表的成本需要花费时间,并且它还占用了大量内存(有效地将您使用的内存加倍)。
因此,如果JVM中的内存不是问题(通常应该是这样),那么您仍需要考虑将两个列表中的每个元素复制到两个TreeSet中所花费的时间。请记住,它会在每个元素进入时对其进行排序。
我的最后建议?在此处做出正确的决策之前,您需要考虑数据集以及数据集中的元素数量,以及数据集中每个对象的大小。与他们一起玩,每个方向创建一个,看看哪个运行得更快。这是一个很好的练习。