Java에서 ArrayList의 교차 및 결합


130

그렇게하는 방법이 있습니까? 찾고 있었지만 찾을 수 없었습니다.

또 다른 질문 : 파일을 필터링 할 수 있도록 이러한 방법이 필요합니다. 일부는 AND필터이고 일부는 OR(이론과 같은) 필터이므로 모든 파일에 따라 필터링해야하며 해당 파일을 보유하는 Unite / intersects ArrayList를 필터링해야합니다.

파일을 보유하기 위해 다른 데이터 구조를 사용해야합니까? 더 나은 런타임을 제공하는 다른 것이 있습니까?


1
새 목록을 만들지 않으려는 경우 Vector.retainAll (Vector)은 두 번째 벡터와의 교차점으로 만 오리진 벡터를 자릅니다.
— user2808054

왜 그런가요 Vector? 이 클래스는 Java 1.2 이후로 권장되지 않습니다.
— dimo414

@ dimo414 내가 사용하는 인터페이스 (옵션 없음)는 벡터로 물건을 반환합니다. 나는 그것이 낙담했다는 것을 몰랐다! 정보 주셔서 감사합니다 .. 누가 낙담합니까? 더 이상 사용되지 않는 것에 대한 메모를 보지 못 했으므로 놀랍습니다
— user2808054

1
Javadocs에서 : " Java 2 플랫폼 v1.2부터 ... Vector 대신 ArrayList를 사용하는 것이 좋습니다. ". 스레드 간 상호 작용 에만 필요할 수도Vector 있지만 해당 사용 사례에 대해 더 안전한 데이터 구조가 있습니다. 이 질문 도 참조하십시오 . Vector2016 년 에도 여전히 사용 중인 모든 도서관 은 제 생각에 매우 의심됩니다.
— dimo414

@ dimo414 IBM 라이브러리입니다. haha! (Lotus Domino 데이터 API). 정보 주셔서 감사합니다, 매우 도움이
— user2808054

답변:


122

다음은 타사 라이브러리를 사용하지 않는 일반 구현입니다. 주요 장점을 통해 retainAll, removeAll그리고 addAll이러한 방법은 방법 원래 목록 입력을 수정하지 않는 것이 있습니다.

public class Test {

    public static void main(String... args) throws Exception {

        List<String> list1 = new ArrayList<String>(Arrays.asList("A", "B", "C"));
        List<String> list2 = new ArrayList<String>(Arrays.asList("B", "C", "D", "E", "F"));

        System.out.println(new Test().intersection(list1, list2));
        System.out.println(new Test().union(list1, list2));
    }

    public <T> List<T> union(List<T> list1, List<T> list2) {
        Set<T> set = new HashSet<T>();

        set.addAll(list1);
        set.addAll(list2);

        return new ArrayList<T>(set);
    }

    public <T> List<T> intersection(List<T> list1, List<T> list2) {
        List<T> list = new ArrayList<T>();

        for (T t : list1) {
            if(list2.contains(t)) {
                list.add(t);
            }
        }

        return list;
    }
}

16
list1 요소를 사용하여 새 목록을 만든 다음 retainAll, addAll 메서드를 호출 할 수 있습니다.
— lukastymo

이 솔루션에서 strictfp를 사용하는 이유는 무엇입니까?
— lukastymo

9
평균 사례 성능이 O (n ^ 2) 대신 O (n)이되도록 HashSetfor를 사용해야합니다 intersection.
— Zong

1
이 게시물은 업데이트를 사용하여 Java 8 Stream API의 이점을 보여줄 수 있습니다.
— SME_Dev September

이 값을 할당하려고하면 오류가 발생합니다.-> 예 : ArrayList <String> total total = (ArrayList <String>) 교차점 (list2, list1) ---> java.util.arraylist를 java.util.arraylist로 캐스트 할 수 없습니다 < 문자열>
— 대인 수수

123

컬렉션 (따라서 ArrayList도)은 다음과 같습니다.

col.retainAll(otherCol) // for intersection
col.addAll(otherCol) // for union

반복을 수락하면 List 구현을 사용하고, 그렇지 않으면 Set 구현을 사용하십시오.

Collection<String> col1 = new ArrayList<String>(); // {a, b, c}
// Collection<String> col1 = new TreeSet<String>();
col1.add("a");
col1.add("b");
col1.add("c");

Collection<String> col2 = new ArrayList<String>(); // {b, c, d, e}
// Collection<String> col2 = new TreeSet<String>();
col2.add("b");
col2.add("c");
col2.add("d");
col2.add("e");

col1.addAll(col2);
System.out.println(col1); 
//output for ArrayList: [a, b, c, b, c, d, e]
//output for TreeSet: [a, b, c, d, e]

3
이 공용체에 "공통 요소가 두 번 포함되므로 올바르지 않습니다" 라는 제안 된 편집이 있었습니다. HashSet대신 편집을 사용하는 것이 좋습니다 .
— 코스

5
실제로 편집되었습니다. "반복을 수락하면 List 구현을 사용하고, 그렇지 않으면 Set 구현을 사용하십시오."
— lukastymo

7
아니요, retainAll은 목록의 교차점이 아닙니다. 위에서 otherCol에없는 col의 모든 요소가 제거됩니다. otherCol이 {a, b, b, c}이고 col이 {b, b, b, c, d}라고 가정 해 봅시다. 그러면 col은 {b, b, b, c}로 끝납니다. 이는 둘의 교차점이 아닙니다. 나는 그것이 {b, b, c} 일 것으로 기대합니다. 다른 작업이 수행되고 있습니다.
— demongolem

1
나는 또한 addAll()목록에 대한 노동 조합이 어떻 습니까? 두 번째 목록을 첫 번째 목록의 끝에 연결합니다. 합집합 연산은 첫 번째 목록에 이미 요소가 포함 된 경우 요소를 추가하지 않습니다.
— dimo414

66

이 게시물은 상당히 오래되었지만 그럼에도 불구하고 해당 주제를 찾을 때 Google에 처음 나타나는 게시물입니다.

Java 8 스트림을 사용하여 한 줄에 (기본적으로) 동일한 작업을 수행하여 업데이트를 제공하고 싶습니다.

List<T> intersect = list1.stream()
    .filter(list2::contains)
    .collect(Collectors.toList());

List<T> union = Stream.concat(list1.stream(), list2.stream())
    .distinct()
    .collect(Collectors.toList());

누구든지 더 나은 / 빠른 솔루션을 가지고 있다면 알려주세요. 그러나이 솔루션은 불필요한 도우미 클래스 / 메소드를 추가하지 않고 메소드에 쉽게 포함시킬 수 있고 여전히 가독성을 유지하는 좋은 라이너입니다.


19
Ooof, 그것은 좋은 한 라이너 일지 모르지만 O (n ^ 2) 시간이 걸립니다. 목록 중 하나를로 변환 한 Set다음 세트의 contains방법 을 사용하십시오 . 인생의 모든 것이 스트림으로 이루어져야하는 것은 아닙니다.
— dimo414

31
list1.retainAll(list2) - is intersection

노동 조합이 될 것입니다 removeAll다음과addAll .

collection (ArrayList is collection) 문서에서 자세한 내용을 확인 하십시오. http://download.oracle.com/javase/1.5.0/docs/api/java/util/Collection.html


1
양 retainAll()및 removeAll()O (N ^ 2)리스트에 동작한다. 우리는 더 잘할 수 있습니다.
— dimo414

1
나는 투표했지만 지금은 질문이 있습니다. retainAll{1, 2, 3}보다 {1, 2, 2, 3, 4, 5} 중 {1, 2, 2, 3}이됩니다. 교차점이 {1, 2, 3}이 아니어야합니까?
— 최규현

21

합집합과 교집합은 목록이 아닌 세트에 대해서만 정의됩니다. 당신이 언급했듯이.

필터는 구아바 라이브러리를 확인하십시오 . 또한 구아바는 실제 교차로와 노조를 제공합니다

 static <E> Sets.SetView<E >union(Set<? extends E> set1, Set<? extends E> set2)
 static <E> Sets.SetView<E> intersection(Set<E> set1, Set<?> set2)

12

당신은 아파치 커먼즈CollectionUtils 에서 사용할 수 있습니다 .


7
누군가 가이 답변을 너무 짧게 발견하면 'CollectionUtils.containsAny'및 'CollectionUtils.containsAll'이 메소드입니다.
— Sebastian

2
아파치 커먼즈의 CollectionUtils가 제네릭을 지원하지 않는 것은 이상하다
— Vasyl Sarzhynskyi

7

표시된 솔루션이 효율적이지 않습니다. 시간 복잡도는 O (n ^ 2)입니다. 우리가 할 수있는 일은 두 목록을 정렬하고 아래와 같이 교차 알고리즘을 실행하는 것입니다.

private  static ArrayList<Integer> interesect(ArrayList<Integer> f, ArrayList<Integer> s) { 
    ArrayList<Integer> res = new ArrayList<Integer>();

    int i = 0, j = 0; 
    while (i != f.size() && j != s.size()) { 

        if (f.get(i) < s.get(j)) {
            i ++;
        } else if (f.get(i) > s.get(j)) { 
            j ++;
        } else { 
            res.add(f.get(i)); 
            i ++;  j ++;
        }
    }


    return res; 
}

이것은 O (n log n)에있는 O (n log n + n)의 복잡성을가집니다. 노조도 비슷한 방식으로 이루어집니다. if-elseif-else 문을 적절히 수정하십시오.

원하는 경우 반복자를 사용할 수도 있습니다 (C ++에서 더 효율적이라는 것을 알고 있습니다 .Java에서도 이것이 사실인지 모르겠습니다).


1
아니 일반적인 충분, T는 비교할 수없는 수 있으며, 경우에 따라서 비교는 ... 비싼
— 보리스 Churzin

일반적이지 않습니다, 전적으로 동의합니다. 비교가 비싸요? 어떻게 해결하겠습니까?
— AJed

슬프게도 - (N ^ 2) : 숫자를 들어이 솔루션은 ... 좋은 O에서 그것을 할 싼 것
— 보리스 Churzin

슬프게도-당신은 내 질문에 대답하지 않았습니다. 다시 말해서, 비용 c (n)의 비교 함수가 주어지면 O (n ^ 2)가 어떻게 더 좋습니까?
— AJed

1
하나의 입력을 세트로 변환 contains()하고 루프에서 호출 하면 (Devenv가 제안한대로) O (n + m) 시간이 걸립니다. 정렬은 불필요하게 복잡하며 O (n log n + m log n + n) 시간이 걸립니다. 그것은 O (n log n) 시간으로 감소하지만, 여전히 선형 시간보다 나쁘고 훨씬 더 복잡합니다.
— dimo414

4

Set교차로를 만들고 결합하려면 파일을 보유하기 위해 a 를 사용해야한다고 생각 합니다. 그럼 당신은 사용할 수 있습니다 구아바 의 세트 클래스 할 수있는 union, intersectiona로하고 필터링 Predicate아니라. 이 방법들과 다른 제안들 간의 차이점은이 모든 방법 들이 두 세트의 결합, 교차점 등에 대한 게으른 견해 를 만든다는 것 입니다. Apache Commons는 새 콜렉션을 작성하고 여기에 데이터를 복사합니다. retainAll컬렉션에서 요소를 제거하여 컬렉션 중 하나를 변경합니다.


4

다음은 스트림과의 교차점을 수행하는 방법입니다 (스트림에 java 8을 사용해야 함을 기억하십시오).

List<foo> fooList1 = new ArrayList<>(Arrays.asList(new foo(), new foo()));
List<foo> fooList2 = new ArrayList<>(Arrays.asList(new foo(), new foo()));
fooList1.stream().filter(f -> fooList2.contains(f)).collect(Collectors.toList());

유형이 다른 목록의 예입니다. foo와 bar 사이에 실현이 있고 스트림을 수정할 수있는 것보다 foo에서 bar 객체를 얻을 수있는 경우 :

List<foo> fooList = new ArrayList<>(Arrays.asList(new foo(), new foo()));
List<bar> barList = new ArrayList<>(Arrays.asList(new bar(), new bar()));

fooList.stream().filter(f -> barList.contains(f.getBar()).collect(Collectors.toList());

3
  • retainAll은 목록을 수정합니다
  • 구아바에는 목록에 대한 API가 없습니다 (세트 전용)

ListUtils 가이 사용 사례에 매우 유용하다는 것을 알았습니다.

기존 목록을 수정하지 않으려면 org.apache.commons.collections에서 ListUtils를 사용하십시오.

ListUtils.intersection(list1, list2)


3

commons-collections4 CollectionUtils를 사용할 수 있습니다

Collection<Integer> collection1 = Arrays.asList(1, 2, 4, 5, 7, 8);
Collection<Integer> collection2 = Arrays.asList(2, 3, 4, 6, 8);

Collection<Integer> intersection = CollectionUtils.intersection(collection1, collection2);
System.out.println(intersection); // [2, 4, 8]

Collection<Integer> union = CollectionUtils.union(collection1, collection2);
System.out.println(union); // [1, 2, 3, 4, 5, 6, 7, 8]

Collection<Integer> subtract = CollectionUtils.subtract(collection1, collection2);
System.out.println(subtract); // [1, 5, 7]

2

Java 8에서는 다음과 같은 간단한 도우미 메서드를 사용합니다.

public static <T> Collection<T> getIntersection(Collection<T> coll1, Collection<T> coll2){
    return Stream.concat(coll1.stream(), coll2.stream())
            .filter(coll1::contains)
            .filter(coll2::contains)
            .collect(Collectors.toSet());
}

public static <T> Collection<T> getMinus(Collection<T> coll1, Collection<T> coll2){
    return coll1.stream().filter(not(coll2::contains)).collect(Collectors.toSet());
}

public static <T> Predicate<T> not(Predicate<T> t) {
    return t.negate();
}

1

목록의 객체가 해시 가능하면 (즉, 적절한 hashCode 및 equals 함수가있는 경우) 테이블 간의 가장 빠른 접근 방법은 약입니다. size> 20은 두 목록 중 더 큰 HashSet을 구성하는 것입니다.

public static <T> ArrayList<T> intersection(Collection<T> a, Collection<T> b) {
    if (b.size() > a.size()) {
        return intersection(b, a);
    } else {
        if (b.size() > 20 && !(a instanceof HashSet)) {
            a = new HashSet(a);
        }
        ArrayList<T> result = new ArrayList();
        for (T objb : b) {
            if (a.contains(objb)) {
                result.add(objb);
            }
        }
        return result;
    }
}

1

나는 비슷한 상황에서 일하고 있었고 여기에 도움을 요청했습니다. Arrays에 대한 내 솔루션을 찾게되었습니다. ArrayList AbsentDates = new ArrayList (); // Array1-Array2를 저장합니다

참고 : 누군가이 페이지를 방문하여 도움을받을 수있는 경우 게시하십시오.

ArrayList<String> AbsentDates = new ArrayList<String>();//This Array will store difference
      public void AbsentDays() {
            findDates("April", "2017");//Array one with dates in Month April 2017
            findPresentDays();//Array two carrying some dates which are subset of Dates in Month April 2017

            for (int i = 0; i < Dates.size(); i++) {

                for (int j = 0; j < PresentDates.size(); j++) {

                    if (Dates.get(i).equals(PresentDates.get(j))) {

                        Dates.remove(i);
                    }               

                }              
                AbsentDates = Dates;   
            }
            System.out.println(AbsentDates );
        }

1

공통 키를 기반으로 한 다른 객체의 두 목록 교차-Java 8

 private List<User> intersection(List<User> users, List<OtherUser> list) {

        return list.stream()
                .flatMap(OtherUser -> users.stream()
                        .filter(user -> user.getId()
                                .equalsIgnoreCase(OtherUser.getId())))
                .collect(Collectors.toList());
    }

이 두 목록의 차이점은 어떻습니까?
— jean

1
public static <T> Set<T> intersectCollections(Collection<T> col1, Collection<T> col2) {
    Set<T> set1, set2;
    if (col1 instanceof Set) {
        set1 = (Set) col1;
    } else {
        set1 = new HashSet<>(col1);
    }

    if (col2 instanceof Set) {
        set2 = (Set) col2;
    } else {
        set2 = new HashSet<>(col2);
    }

    Set<T> intersection = new HashSet<>(Math.min(set1.size(), set2.size()));

    for (T t : set1) {
        if (set2.contains(t)) {
            intersection.add(t);
        }
    }

    return intersection;
}

JDK8 + (아마도 최고의 성능)

public static <T> Set<T> intersectCollections(Collection<T> col1, Collection<T> col2) {
    boolean isCol1Larger = col1.size() > col2.size();
    Set<T> largerSet;
    Collection<T> smallerCol;

    if (isCol1Larger) {
        if (col1 instanceof Set) {
            largerSet = (Set<T>) col1;
        } else {
            largerSet = new HashSet<>(col1);
        }
        smallerCol = col2;
    } else {
        if (col2 instanceof Set) {
            largerSet = (Set<T>) col2;
        } else {
            largerSet = new HashSet<>(col2);
        }
        smallerCol = col1;
    }

    return smallerCol.stream()
            .filter(largerSet::contains)
            .collect(Collectors.toSet());
}

성능에 신경 쓰지 않고 더 작은 코드를 선호하는 경우 다음을 사용하십시오.

col1.stream().filter(col2::contains).collect(Collectors.toList());

0

마지막 해결책:

//all sorted items from both
public <T> List<T> getListReunion(List<T> list1, List<T> list2) {
    Set<T> set = new HashSet<T>();
    set.addAll(list1);
    set.addAll(list2);
    return new ArrayList<T>(set);
}

//common items from both
public <T> List<T> getListIntersection(List<T> list1, List<T> list2) {
    list1.retainAll(list2);
    return list1;
}

//common items from list1 not present in list2
public <T> List<T> getListDifference(List<T> list1, List<T> list2) {
    list1.removeAll(list2);
    return list1;
}

0

먼저 배열의 모든 값을 단일 배열로 복사 한 다음 중복 값을 배열로 제거합니다. 12 번 줄, 같은 숫자가 시간보다 많이 발생하는지 설명하고 여분의 가비지 값을 "j"위치에 넣습니다. 마지막에, 시작점에서 순회하여 동일한 가비지 값이 발생하는지 확인한 후 폐기하십시오.

public class Union {
public static void main(String[] args){

    int arr1[]={1,3,3,2,4,2,3,3,5,2,1,99};
    int arr2[]={1,3,2,1,3,2,4,6,3,4};
    int arr3[]=new int[arr1.length+arr2.length];

    for(int i=0;i<arr1.length;i++)
        arr3[i]=arr1[i];

    for(int i=0;i<arr2.length;i++)
        arr3[arr1.length+i]=arr2[i];
    System.out.println(Arrays.toString(arr3));

    for(int i=0;i<arr3.length;i++)
    {
        for(int j=i+1;j<arr3.length;j++)
        {
            if(arr3[i]==arr3[j])
                arr3[j]=99999999;          //line  12
        }
    }
    for(int i=0;i<arr3.length;i++)
    {
        if(arr3[i]!=99999999)
            System.out.print(arr3[i]+" ");
    }
}   
}

1
스택 오버플로에 오신 것을 환영합니다! 질문은 ArrayList에 관한 것입니다. 또한이 특정 구현으로 인해 원하는 것이 남아있을 것 같습니다. 센티넬로 사용되는 값 99999999가 입력에서 발생할 수 있습니다. ArrayList공용체의 결과를 저장하기 위해 와 같은 동적 구조를 사용하는 것이 좋습니다 .
— SL Barth-복원 모니카

1
코드 답변 대신 제시 한 코드를 설명하십시오.
— tmarois 2016 년

나는 당신이 쓰레기 값을 넣어야한다는 단서를주고 있습니다
— Ashutosh

설명을 추가하게되어 기쁩니다. 불행히도 대답 자체는 여전히 나쁩니다. 배열을 사용할 이유가 없습니다. ArrayList와 같은 동적 구조를 사용해야합니다. (어떤 이유로 든) 배열을 사용해야 Integer하는 경우 대신 의 배열 사용을 고려해야 int합니다. 그런 다음 null"쓰레기 값"대신 사용할 수 있습니다 . "가비지 값"또는 "센티넬 값"은 입력에서 여전히 발생할 수 있으므로 일반적으로 나쁜 생각입니다.
— SL Barth-복원 모니카

0

테스트 후 여기에 가장 좋은 교차 접근법이 있습니다.

순수한 HashSet 접근 방식에 비해 빠른 속도. 아래의 HashSet 및 HashMap은 1 백만 개가 넘는 레코드가있는 어레이에 대해 유사한 성능을 갖습니다.

Java 8 Stream 접근 방식의 경우 배열 크기가 10k보다 큰 경우 속도가 매우 느립니다.

이것이 도움이되기를 바랍니다.

public static List<String> hashMapIntersection(List<String> target, List<String> support) {
    List<String> r = new ArrayList<String>();
    Map<String, Integer> map = new HashMap<String, Integer>();
    for (String s : support) {
        map.put(s, 0);
    }
    for (String s : target) {
        if (map.containsKey(s)) {
            r.add(s);
        }
    }
    return r;
}
public static List<String> hashSetIntersection(List<String> a, List<String> b) {
    Long start = System.currentTimeMillis();

    List<String> r = new ArrayList<String>();
    Set<String> set = new HashSet<String>(b);

    for (String s : a) {
        if (set.contains(s)) {
            r.add(s);
        }
    }
    print("intersection:" + r.size() + "-" + String.valueOf(System.currentTimeMillis() - start));
    return r;
}

public static void union(List<String> a, List<String> b) {
    Long start = System.currentTimeMillis();
    Set<String> r= new HashSet<String>(a);
    r.addAll(b);
    print("union:" + r.size() + "-" + String.valueOf(System.currentTimeMillis() - start));
}


-1

세트에 데이터가 있다면 Guava의 Sets클래스를 사용할 수 있습니다 .


-1

내가 확인하는 것보다 숫자가 일치하면 처음으로 발생하거나 "indexOf ()"의 도움으로 숫자가 처음으로 일치하면 인쇄하고 문자열에 저장하여 다음 번에 같은 숫자가 일치하면 ' "indexOf ()"조건으로 인해 인쇄가 실패합니다.

class Intersection
{
public static void main(String[] args)
 {
  String s="";
    int[] array1 = {1, 2, 5, 5, 8, 9, 7,2,3512451,4,4,5 ,10};
    int[] array2 = {1, 0, 6, 15, 6, 5,4, 1,7, 0,5,4,5,2,3,8,5,3512451};


       for (int i = 0; i < array1.length; i++)
       {
           for (int j = 0; j < array2.length; j++)
           {
               char c=(char)(array1[i]);
               if(array1[i] == (array2[j])&&s.indexOf(c)==-1)
               {    
                System.out.println("Common element is : "+(array1[i]));
                s+=c;
                }
           }
       }    
}

}


2
답변으로 코드를 게시하지 말고 현재하고있는 일에 대해 조금만 설명
— 해주세요

그것은 내가 처음으로 업로드 한 프로그램입니다
— Ashutosh

2
이 코드는 문제를 해결하는 데 도움이 될 수 있지만 질문에 왜 그리고 / 또는 어떻게 대답 하는지 는 설명하지 않습니다 . 이 추가 상황을 제공하면 장기적인 가치가 크게 향상됩니다. 제발 편집 제한 및 가정이 적용되는 것을 포함하여, 설명을 추가 답변을.
— Toby Speight
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.