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.