How to compare equality of lists of arrays with modern Java?

后端 未结 5 1583
清酒与你
清酒与你 2021-01-31 07:18

I have two lists of arrays.

How do I easily compare equality of these with Java 8 and its features, without using external libraries? I am looking for a \"bett

相关标签:
5条回答
  • 2021-01-31 07:32

    1) Solution based on Java 8 streams:

    List<List<String>> first = list1.stream().map(Arrays::asList).collect(toList());
    List<List<String>> second = list2.stream().map(Arrays::asList).collect(toList());
    return first.equals(second);
    

    2) Much simpler solution (works in Java 5+):

    return Arrays.deepEquals(list1.toArray(), list2.toArray());
    

    3) Regarding your new requirement (to check the contained String arrays length first), you could write a generic helper method that does equality check for transformed lists:

    <T, U> boolean equal(List<T> list1, List<T> list2, Function<T, U> mapper) {
        List<U> first = list1.stream().map(mapper).collect(toList());
        List<U> second = list2.stream().map(mapper).collect(toList());
        return first.equals(second);
    }
    

    Then the solution could be:

    return equal(list1, list2, s -> s.length)
        && equal(list1, list2, Arrays::asList);
    
    0 讨论(0)
  • 2021-01-31 07:33

    You could use a stream if the lists are random access lists (so that a call to get is fast - generally constant time) leading to:

    //checks for null and size before
    boolean same = IntStream.range(0, list1.size()).allMatch(i -> Arrays.equals(list1.get(i), list2.get(i)));
    

    However, you might give as parameters some implementations that are not (such as LinkedLists). In this case, the best way is to use the iterator explicitly. Something like:

    boolean compare(List<String[]> list1, List<String[]> list2) {
    
        //checks for null and size
    
        Iterator<String[]> iteList1 = list1.iterator();
        Iterator<String[]> iteList2 = list2.iterator();
    
        while(iteList1.hasNext()) {
            if(!Arrays.equals(iteList1.next(), iteList2.next())) {
                return false;
            }
        }
        return true;
    }
    
    0 讨论(0)
  • 2021-01-31 07:36

    You could stream over one list and compare to each element of the other by using an iterator:

    Iterator<String[]> it = list1.iterator();
    boolean match = list1.size() == list2.size() &&
                    list2.stream().allMatch(a -> Arrays.equals(a, it.next()));
    

    Using an iterator instead of the get(index) method on the first list is better because it doesn't matter whether the list is RandomAccess or not.

    Note: this only works with a sequential stream. Using a parallel stream will lead to wrong results.


    EDIT: As per the question last edit, which indicates it would be better to check the length of every pair of arrays in advance, I think it could be achieved with a slight modification to my previous code:

    Iterator<String[]> itLength = list1.iterator();
    Iterator<String[]> itContents = list1.iterator();
    
    boolean match = 
            list1.size() == list2.size()
        && 
            list2.stream()
                .allMatch(a -> {
                    String[] s = itLength.next();
                    return s == null ? a == null :
                           a == null ? s == null :
                           a.length == s.length;
                })
        && 
            list2.stream()
                .allMatch(a -> Arrays.equals(a, itContents.next()));
    

    Here I'm using two iterators and am streaming list2 twice, but I see no other way to check all lengths before checking the contents of the first pair of arrays. Check for lengths is null-safe, while check for contents is delegated to the Arrays.equals(array1, array2) method.

    0 讨论(0)
  • 2021-01-31 07:46

    The for loop at least can be streamified, leading to:

    return (list1.size()==list2.size() &&
            IntStream.range(0, list1.size())
                     .allMatch(i -> Arrays.equals(list1.get(i), list2.get(i)));
    
    0 讨论(0)
  • 2021-01-31 07:46

    using zip (which originates from lambda b93) function from https://stackoverflow.com/a/23529010/755183, code could look like:

    boolean match = a.size() == b.size() && 
                    zip(a.stream(), b.stream(), Arrays::deepEquals).
                    allMatch(equal -> equal)
    

    update

    in order to check size of arrays first and then content this could be a solution to consider

    final boolean match = a.size() == b.size() 
                       && zip(a.stream(), b.stream(), (as, bs) -> as.length == bs.length).
                          allMatch(equal -> equal)
                       && zip(a.stream(), b.stream(), Arrays::deepEquals).
                          allMatch(equal -> equal);
    
    0 讨论(0)
提交回复
热议问题