List.equals和CollectionUtils.isEqualCollection的区别

1,110 阅读1分钟

源码

List.equals源码

public boolean equals(Object o) {
    if (o == this) {
        return true;
    }
    if (!(o instanceof List)) {
        return false;
    }
    final int expectedModCount = modCount;
    // ArrayList can be subclassed and given arbitrary behavior, but we can
    // still deal with the common case where o is ArrayList precisely
    boolean equal = (o.getClass() == ArrayList.class)
        ? equalsArrayList((ArrayList<?>) o)
        : equalsRange((List<?>) o, 0, size);
    checkForComodification(expectedModCount);
    return equal;
}


private boolean equalsArrayList(ArrayList<?> other) {
    final int otherModCount = other.modCount;
    final int s = size;
    boolean equal;
    if (equal = (s == other.size)) {
        final Object[] otherEs = other.elementData;
        final Object[] es = elementData;
        if (s > es.length || s > otherEs.length) {
            throw new ConcurrentModificationException();
        }
        for (int i = 0; i < s; i++) {
            if (!Objects.equals(es[i], otherEs[i])) {
                equal = false;
                break;
            }
        }
    }
    other.checkForComodification(otherModCount);
    return equal;
}

底层还是用Objects.equals、再往底层了说还是用list里元素的自己本身的equals方法进行比较的
Objects.equals(es[i], otherEs[i])

而且比较依赖元素的顺序

CollectionUtils.isEqualCollection源码

// 对于如下依赖的源码
<dependency>
    <groupId>commons-collections</groupId>
    <artifactId>commons-collections</artifactId>
    <version>3.2.2</version>
</dependency>


public static boolean isEqualCollection(final Collection a, final Collection b) {
    if(a.size() != b.size()) {
        return false;
    } else {
        Map mapa = getCardinalityMap(a);
        Map mapb = getCardinalityMap(b);
        if(mapa.size() != mapb.size()) {
            return false;
        } else {
            Iterator it = mapa.keySet().iterator();
            while(it.hasNext()) {
                Object obj = it.next();
                if(getFreq(obj,mapa) != getFreq(obj,mapb)) {
                    return false;
                }
            }
            return true;
        }
    }
}

public static Map getCardinalityMap(final Collection coll) {
    Map count = new HashMap();
    for (Iterator it = coll.iterator(); it.hasNext();) {
        Object obj = it.next();
        Integer c = (Integer) (count.get(obj));
        if (c == null) {
            count.put(obj,INTEGER_ONE);
        } else {
            count.put(obj,new Integer(c.intValue() + 1));
        }
    }
    return count;
}


public V get(Object key) {
    Node<K,V> e;
    return (e = getNode(key)) == null ? null : e.value;
}
 
private static final int getFreq(final Object obj, final Map freqMap) {
    Integer count = (Integer) freqMap.get(obj);
    if (count != null) {
        return count.intValue();
    }
    return 0;
}

这个底层不是依赖equals、而是通过元素生成HashCode来比较、哪怕是个对象也会用这个对象去生成一个HashCode

hashMap的key的总数是一个比较点、这个相同说明不重复的元素个数相同
key相同的count又是一个比较点、这个相同说明重复的元素的个数相同
// 对于如下依赖的源码
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.4</version>
</dependency>

待补充

Demo 举例

equals

ArrayList<String> listA = new ArrayList<>() {{
    add("a");
    add("b");
    add("c");
}};
ArrayList<String> listB = new ArrayList<>() {{
    add("a");
    add("b");
    add("c");
}};
System.out.println(listA.equals(listB)); // true
ArrayList<String> listA = new ArrayList<>() {{
    add("a");
    add("b");
    add("c");
}};
ArrayList<String> listB = new ArrayList<>() {{
    add("b");
    add("a");
    add("c");
}};
System.out.println(listA.equals(listB)); // false
ArrayList<String> listA = new ArrayList<>() {{
    add("a");
    add("b");
    add("c");
}};
ArrayList<String> listB = new ArrayList<>() {{
    add("a");
    add("b");
}};
System.out.println(listA.equals(listB));  // false
ArrayList<String> listA = new ArrayList<>();
ArrayList<String> listB = new ArrayList<>();
System.out.println(listA.equals(listB));  // true
ArrayList<String> listA = new ArrayList<>() {{
    add("a");
    add("b");
    add("c");
}};
System.out.println(listA.equals(null));  // false

结论:

1、顺序不同则视为不同

// 先排序再比较
Collections.sort(listA);
Collections.sort(listB);
System.out.println(listA.equals(listB));// true

CollectionUtils.isEqualCollection

ArrayList<String> listA = new ArrayList<>() {{
    add("a");
    add("b");
    add("c");
}};
ArrayList<String> listB = new ArrayList<>() {{
    add("a");
    add("b");
    add("c");
}};
System.out.println(CollectionUtils.isEqualCollection(listA, listB)); // true
ArrayList<String> listA = new ArrayList<>() {{
    add("a");
    add("b");
    add("c");
}};
ArrayList<String> listB = new ArrayList<>() {{
    add("c");
    add("a");
    add("b");
}};
System.out.println(CollectionUtils.isEqualCollection(listA, listB)); // true
ArrayList<String> listA = new ArrayList<>() {{
    add("a");
    add("b");
    add("c");
}};
ArrayList<String> listB = new ArrayList<>() {{
    add("a");
    add("b");
}};
System.out.println(CollectionUtils.isEqualCollection(listA, listB)); // false
ArrayList<String> listA = new ArrayList<>();
ArrayList<String> listB = new ArrayList<>();
System.out.println(CollectionUtils.isEqualCollection(listA, listB)); //true
ArrayList<String> listA = new ArrayList<>();
System.out.println(CollectionUtils.isEqualCollection(listA, null));

java.lang.NullPointerException: Cannot invoke "java.util.Collection.size()" because "b" is null

结论:

1、比较会忽略顺序、只比较内容

2、不能比较null、报空指针