[java] Java ArrayList-순서가 중요하지 않은 두 목록이 동일한 지 어떻게 알 수 있습니까?

나는 두 가지 ArrayList유형의 Answer(자체 수업)을 가지고 있습니다.

두 목록을 비교하여 동일한 내용이 포함되어 있는지, 순서가 중요하지 않은지 확인하고 싶습니다.

예:

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

List.equals동일한 크기, 내용 및 요소 순서를 포함하는 경우 두 목록이 동일 함을 나타냅니다. 나는 똑같은 것을 원하지만 순서는 중요하지 않습니다.

이를 수행하는 간단한 방법이 있습니까? 아니면 중첩 된 for 루프를 수행하고 두 목록의 각 인덱스를 수동으로 확인해야합니까?

참고 : ArrayList다른 유형의 목록으로 변경할 수 없으므로 그대로 유지해야합니다.



답변

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);
}


답변

아마 가장 쉬운 방법은 모든 목록은 다음과 같습니다

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)

true주어진 Collections에 정확히 동일한 카디널리티가있는 동일한 요소가 포함되어 있으면를
반환 합니다.

즉, 카디널리티 IFF에 전자 에 카디널리티 같다 에서 , B 각각의 요소에 대해, E 에 또는 B .

매개 변수 :

  • a -첫 번째 컬렉션은 null
  • b -두 번째 컬렉션은 null

반환 값 : true컬렉션이 동일한 카디널리티와 같은 요소를 포함 IFF에.


답변

// 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;
}


답변

이 답변에 속임수가 없다고 말하고 싶습니다.

Bloch는 필수적이고 훌륭하고 간결한 Effective Java 에서 항목 47에서 “라이브러리를 알고 사용하십시오”라는 제목으로 “바퀴를 재발 명하지 마십시오”라고 말합니다. 그리고 그는 몇 가지 매우 분명한 이유를 제시합니다.

여기 CollectionUtils에 Apache Commons Collections 라이브러리의 메소드를 제안하는 몇 가지 답변이 있지만 이 질문에 대답하는 가장 아름답고 우아한 방법은 없습니다 .

Collection<Object> culprits = CollectionUtils.disjunction( list1, list2 );
if( ! culprits.isEmpty() ){
  // ... do something with the culprits, i.e. elements which are not common

}

Culprits : 즉 둘 다 공통적이지 않은 요소 Lists. 에 속하는 범인 결정 list1하고있는 list2비교적 간단 사용 CollectionUtils.intersection( list1, culprits )하고 CollectionUtils.intersection( list2, culprits ).
그러나 { “a”, “a”, “b”} disjunction와 { “a”, “b”, “b”}와 같은 경우에는 소프트웨어가 실패하지 않는 것을 제외하고는 차이가 있습니다. 원하는 작업의 미묘함 / 모호함의 본질.

Apache 엔지니어가 생성 한 것과 같은 작업에 대해서는 항상 소스 코드 (l. 287)를 검사 할 수 있습니다 . 코드를 사용하면 얻을 수있는 이점 중 하나는 코드를 철저히 시도하고 테스트했을뿐 아니라 여러 가지 중요한 사례와 문제가 예상되고 처리된다는 것입니다. 필요한 경우이 코드를 복사하여 마음의 내용에 맞게 조정할 수 있습니다.


NB 저는 처음에는 CollectionUtils자신에게 직접 적용 할 수있는 오버로드 된 버전을 제공하는 방법 이 없다는 것에 실망했습니다 Comparator(따라서 equals목적에 맞게 재정의 할 수 있음 ).

그러나 collections4 4.0에는 Equator“T 유형의 객체 간 동등성을 결정하는” 새로운 클래스가 있습니다. collections4 CollectionUtils.java의 소스 코드를 살펴보면 몇 가지 메소드와 함께 이것을 사용하는 것 같지만 CardinalityHelper클래스를 사용하여 파일 상단의 메소드에는 적용 할 수 없습니다 … 포함 disjunction하고 intersection.

나는 그것이 아닌 사소한 때문에 아파치 사람들이 아직이 주위에 도착하지 않은 것으로 추측 : 당신이 대신 요소의 고유 사용하는 “AbstractEquatingCollection”클래스, 같은 것을 만들어야 할 것입니다 equalshashCode방법을 대신들을 사용하는 것을 의 Equator와 같은 모든 기본적인 방법에 대한 add, contains등 NB는 소스 코드를 볼 때 실제로 AbstractCollection구현하지 않습니다 add,도 등의 추상적 인 서브 클래스를 할 AbstractSet… 당신과 같은 구체적인 클래스까지 기다려야 HashSetArrayList이전 add구현됩니다. 꽤 두통.

그동안이 공간을 지켜봐야한다고 생각합니다. 분명한 임시 해결책은 모든 요소를 ​​맞춤형 래퍼 클래스로 래핑하여 원하는 평등 을 사용 equals하고 hashCode구현 한 Collections다음 래퍼 객체 를 조작하는 것 입니다.


답변

항목의 카디널리티가 중요하지 않은 경우 (즉, 반복되는 요소가 하나로 간주 됨) 정렬하지 않고이를 수행 할 수있는 방법이 있습니다.

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

이것은 생성됩니다 Set각각의 아웃 List한 다음 사용 HashSetequals방법을 무시합니다 (물론) 순서를.

카디널리티가 중요한 경우, 귀하는 다음과 같은 시설을 제공해야합니다 List. 이 경우 @jschoen의 대답이 더 적합합니다.


답변

목록을 Guava의 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));