두 ArrayList의 차이를 어떻게 계산할 수 있습니까?


81

두 개의 ArrayList가 있습니다.

ArrayList A에는 다음이 포함됩니다.

['2009-05-18','2009-05-19','2009-05-21']

ArrayList B에는 다음이 포함됩니다.

['2009-05-18','2009-05-18','2009-05-19','2009-05-19','2009-05-20','2009-05-21','2009-05-21','2009-05-22']

ArrayList A와 ArrayList B를 비교해야합니다. 결과 ArrayList에는 ArrayList A에없는 List가 포함되어야합니다.

ArrayList 결과는 다음과 같아야합니다.

['2009-05-20','2009-05-22']

비교하는 방법?

답변:


193

Java에서는 Collection인터페이스의 removeAll메소드를 사용할 수 있습니다 .

// Create a couple ArrayList objects and populate them
// with some delicious fruits.
Collection firstList = new ArrayList() {{
    add("apple");
    add("orange");
}};

Collection secondList = new ArrayList() {{
    add("apple");
    add("orange");
    add("banana");
    add("strawberry");
}};

// Show the "before" lists
System.out.println("First List: " + firstList);
System.out.println("Second List: " + secondList);

// Remove all elements in firstList from secondList
secondList.removeAll(firstList);

// Show the "after" list
System.out.println("Result: " + secondList);

위의 코드는 다음 출력을 생성합니다.

First List: [apple, orange]
Second List: [apple, orange, banana, strawberry]
Result: [banana, strawberry]

7
목록이 사용자 지정 클래스 인 경우 클래스의 equals 메서드를 재정의해야합니다.
RTF

5
@RTF 예, equals개체를 비교할 수 있는 구현을 제공해야합니다 . 구현 hashCode에 대해서도 읽어보십시오 . 예를 들어, 어떻게 참고 String::equals대소 문자 구분은 , 그래서 "사과"와 "애플은"같은 고려되지 않습니다.
Basil Bourque

1
사실 대답은 당신이하고 싶은 일에 달려 있습니다. RemoveAll은 중복을 유지하지 않습니다. 두 번째 목록에 다른 "사과"문자열을 추가하면 해당 문자열도 제거되므로 항상 원하는 것이 아닐 수 있습니다.
jules testard

2
이것은 너무 비효율적입니다. 이것은 선택된 답변이자 최고 평점을받은 답변입니다. removeAllfirstList.contains모든 요소를 호출 합니다 secondList. a를 사용하면 HashSet이를 방지 할 수 있으며 몇 가지 좋은 답변이 낮습니다.
Vlasec


12

스트림이있는 Java 8에서는 실제로 매우 간단합니다. 편집 : 스트림없이 효율적일 수 있습니다.

List<String> listA = Arrays.asList("2009-05-18","2009-05-19","2009-05-21");
List<String> listB = Arrays.asList("2009-05-18","2009-05-18","2009-05-19","2009-05-19",
                                   "2009-05-20","2009-05-21","2009-05-21","2009-05-22");

List<String> result = listB.stream()
                           .filter(not(new HashSet<>(listA)::contains))
                           .collect(Collectors.toList());

해시 세트는 한 번만 생성됩니다. 메서드 참조는 contains 메서드에 연결됩니다. 람다로 동일한 작업을 수행하려면 변수에 세트가 있어야합니다. 변수를 만드는 것은 나쁜 생각이 아닙니다. 특히보기 흉하거나 이해하기 어렵다면 더욱 그렇습니다.

네 게이트 메서드 참조를 직접 호출 할 수 없기 때문에이 유틸리티 메서드 (또는 명시 적 캐스트)없이 술어 를 쉽게 부정 할 수 없습니다 (먼저 유형 추론이 필요함).

private static <T> Predicate<T> not(Predicate<T> predicate) {
    return predicate.negate();
}

스트림에 filterOut메서드 나 무언가 가 있으면 더 좋아 보일 것입니다.


또한 @Holger가 나에게 아이디어를 주었다. ArrayList그있다 removeAll방법은 여러 제거를 위해 최적화 된, 그것은 단지 요소 번 재 배열. 그러나 contains주어진 컬렉션에서 제공 하는 방법을 사용 하므로 listA작은 부분이 아닌 경우 해당 부분을 최적화해야합니다 .

listAlistB이전에 선언이 솔루션은 자바 8 필요하지 않습니다 그리고 그것은 매우 효율적입니다.

List<String> result = new ArrayList(listB);
result.removeAll(new HashSet<>(listA));

1
@Bax 왜 편집? 원본은 깨끗하고 기능적으로 동일했습니다.
shmosel

1
@Bax 아니, 그렇지 않습니다.
shmosel

1
Guava를 사용하면 Predicates.in(new HashSet<>(listA)).negate().
shmosel

1
몇 가지 테스트를 실행하고이 솔루션은 listB.removeAll (new HashSet <> (listA))보다 10-20 % 빠릅니다. 및 Guava Sets.difference (...) si 스트림보다 2 배 느립니다.
telebog

1
@Vlasec ArrayList.remove은 선형 복잡도를 가지고 있지만 ArrayList.removeAll의존하지 않고 remove선형 배열 업데이트 작업을 수행하여 나머지 요소를 최종 위치에 복사합니다. 반대로의 참조 구현은 LinkedList최적화 removeAll되지 않았지만 remove영향을받는 각 요소에 대해 작업을 수행하며 매번 최대 5 개의 참조를 업데이트합니다. 따라서 제거 된 요소와 나머지 요소 사이의 비율에 따라 ArrayList's '는 거대한 목록의 경우에도 's removeAll보다 훨씬 더 잘 수행 될 수 있습니다 LinkedList.
Holger

9

편집 : 원래 질문은 언어를 지정하지 않았습니다. 내 대답은 C #입니다.

대신이 목적으로 HashSet을 사용해야합니다. ArrayList를 사용해야하는 경우 다음 확장 메서드를 사용할 수 있습니다.

var a = arrayListA.Cast<DateTime>();
var b = arrayListB.Cast<DateTime>();    
var c = b.Except(a);

var arrayListC = new ArrayList(c.ToArray());

HashSet 사용 ...

var a = new HashSet<DateTime>(); // ...and fill it
var b = new HashSet<DateTime>(); // ...and fill it
b.ExceptWith(a); // removes from b items that are in a


8

이것은 Java 8에서 매우 오래된 질문이지만 다음과 같이 할 수 있습니다.

 List<String> a1 = Arrays.asList("2009-05-18", "2009-05-19", "2009-05-21");
 List<String> a2 = Arrays.asList("2009-05-18", "2009-05-18", "2009-05-19", "2009-05-19", "2009-05-20", "2009-05-21","2009-05-21", "2009-05-22");

 List<String> result = a2.stream().filter(elem -> !a1.contains(elem)).collect(Collectors.toList());

저는 Java 8을 좋아하지만 여전히 복잡성을 생각해야합니다. 목록에도 Collection의 방법 이 있지만 contains매우 비효율적입니다. 찾을 수없는 경우 전체 목록을 통과해야합니다. 의 모든 요소에 대해 수행하는 것은 a2큰 목록에서 고통스럽게 느려질 수 있으므로 a1내 대답에 세트를 만듭니다 .
Vlasec

2

나는 당신이 C #에 대해 이야기하고 있다고 생각합니다. 그렇다면 이것을 시도 할 수 있습니다.

    ArrayList CompareArrayList(ArrayList a, ArrayList b)
    {
        ArrayList output = new ArrayList();
        for (int i = 0; i < a.Count; i++)
        {
            string str = (string)a[i];
            if (!b.Contains(str))
            {
                if(!output.Contains(str)) // check for dupes
                    output.Add(str);
            }
        }
        return output;
    }

죄송하지만 progrsmming 언어를 언급하지 않았습니다. 괜찮습니다.하지만 재생을위한 자바 감사가 필요합니다
naveen

맞습니다. 그것은 또한 그것을 수행하는 매우 비효율적 인 방법입니다. 기본적으로 전체 b목록 a.Count시간을 순환합니다 . HashSet대신에를 만들 Contains거나 RemoveAll세트 에서 메서드를 사용하여 원하는 결과를 정확하게 얻을 수 있습니다.
Vlasec

1

문자열을 비교하고 있습니다.

ArrayList A의 값을 HashTable A의 키로
넣습니다. ArrayList B의 값을 HashTable B의 키로 넣습니다.

그런 다음 HashTable A의 각 키에 대해 존재하는 경우 HashTable B에서 제거합니다.

HashTable B에 남은 것은 ArrayList A의 값이 아닌 문자열 (키)입니다.

코드 요청에 대한 응답으로 추가 된 C # (3.0) 예제 :

List<string> listA = new List<string>{"2009-05-18","2009-05-19","2009-05-21'"};
List<string> listB = new List<string>{"2009-05-18","2009-05-18","2009-05-19","2009-05-19","2009-05-20","2009-05-21","2009-05-21","2009-05-22"};

HashSet<string> hashA = new HashSet<string>();
HashSet<string> hashB = new HashSet<string>();

foreach (string dateStrA in listA) hashA.Add(dateStrA);
foreach (string dateStrB in listB) hashB.Add(dateStrB);

foreach (string dateStrA in hashA)
{
    if (hashB.Contains(dateStrA)) hashB.Remove(dateStrA);
}

List<string> result = hashB.ToList<string>();

C # 코드에서 hashA변수는 사실상 쓸모가 없습니다. 반복되고 호출되지 않으므로 listA대신 foreach를 만들 수 있습니다. hashAContains
Vlasec

(또한 C #에 Java와 같은 RemoveAll 메서드가있는 경우 자체 사이클을 만드는 것을 피할 수 있습니다.하지만이 솔루션이 선택한 솔루션보다 적어도 훨씬 더 효율적이기 때문에 저는 여러분을
찬성했습니다

1

안녕하세요이 클래스를 사용하면 두 목록을 비교하고 두 목록의 불일치를 정확히 보여줍니다.

import java.util.ArrayList;
import java.util.List;


public class ListCompare {

    /**
     * @param args
     */
    public static void main(String[] args) {
        List<String> dbVinList;
        dbVinList = new ArrayList<String>();
        List<String> ediVinList;
        ediVinList = new ArrayList<String>();           

        dbVinList.add("A");
        dbVinList.add("B");
        dbVinList.add("C");
        dbVinList.add("D");

        ediVinList.add("A");
        ediVinList.add("C");
        ediVinList.add("E");
        ediVinList.add("F");
        /*ediVinList.add("G");
        ediVinList.add("H");
        ediVinList.add("I");
        ediVinList.add("J");*/  

        List<String> dbVinListClone = dbVinList;
        List<String> ediVinListClone = ediVinList;

        boolean flag;
        String mismatchVins = null;
        if(dbVinListClone.containsAll(ediVinListClone)){
            flag = dbVinListClone.removeAll(ediVinListClone);   
            if(flag){
                mismatchVins = getMismatchVins(dbVinListClone);
            }
        }else{
            flag = ediVinListClone.removeAll(dbVinListClone);
            if(flag){
                mismatchVins = getMismatchVins(ediVinListClone);
            }
        }
        if(mismatchVins != null){
            System.out.println("mismatch vins : "+mismatchVins);
        }       

    }

    private static String getMismatchVins(List<String> mismatchList){
        StringBuilder mismatchVins = new StringBuilder();
        int i = 0;
        for(String mismatch : mismatchList){
            i++;
            if(i < mismatchList.size() && i!=5){
                mismatchVins.append(mismatch).append(",");  
            }else{
                mismatchVins.append(mismatch);
            }
            if(i==5){               
                break;
            }
        }
        String mismatch1;
        if(mismatchVins.length() > 100){
            mismatch1 = mismatchVins.substring(0, 99);
        }else{
            mismatch1 = mismatchVins.toString();
        }       
        return mismatch1;
    }

}

클론이 실제로 클론이 아니라는 것을 알고 계셨습니까?
Vlasec

1

이것은 또한 Arraylist와 함께 작동합니다.

    // Create a couple ArrayList objects and populate them
    // with some delicious fruits.
    ArrayList<String> firstList = new ArrayList<String>() {/**
         * 
         */
        private static final long serialVersionUID = 1L;

    {
        add("apple");
        add("orange");
        add("pea");
    }};

    ArrayList<String> secondList = new ArrayList<String>() {

    /**
         * 
         */
        private static final long serialVersionUID = 1L;

    {
        add("apple");
        add("orange");
        add("banana");
        add("strawberry");
    }};

    // Show the "before" lists
    System.out.println("First List: " + firstList);
    System.out.println("Second List: " + secondList);

    // Remove all elements in firstList from secondList
    secondList.removeAll(firstList);

    // Show the "after" list
    System.out.println("Result: " + secondList);

1
출력 : 첫 번째 목록 : [사과, 오렌지, 피포] 두 번째 목록 : [사과, 오렌지, 바나나, 딸기] 결과 : [바나나, 딸기]
psycho

그렇습니다. 그러나 그렇게 말할 때 큰 목록에서 고통스럽게 느려질 수 있다는 점을 잊지 말아야합니다. 메소드 는 전체 목록을 좋아 remove하고 contains검색해야합니다. 주기에서 반복적으로 호출되면 (에서 발생 removeAll) 2 차 복잡도를 얻습니다. 그러나 해시 세트를 사용하고 선형으로 만들 수 있습니다.
블 라섹
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.