Java中ArrayList的交集和并集


130

有什么方法可以这样做吗?我正在寻找,但找不到任何东西。

另一个问题:我需要这些方法,以便可以过滤文件。有些是AND过滤器,有些是OR过滤器(就像在集合论中一样),因此我需要根据所有文件以及包含这些文件的unite / intersects ArrayLists进行过滤。

我是否应该使用其他数据结构来保存文件?还有其他什么可以提供更好的运行时间吗?


1
如果您不想创建新列表,则Vector.retainAll(Vector)会将原始矢量修剪为仅与第二个矢量的交集。
user2808054

@ user2808054为什么Vector?从Java 1.2开始不鼓励使用该类。
dimo414

@ dimo414我正在使用的接口(我没有选择)将事物返回为向量。我不知道那是气disc!感谢您提供的信息..谁劝阻?我还没有看到有关它被弃用的记录,所以这是一个惊喜
user2808054

1
在Javadocs中:“ 从Java 2平台v1.2开始,建议使用ArrayList代替Vector。 ”。您可能唯一需要的Vector是跨线程交互,但是对于那些用例,也有更安全的数据结构。另请参阅此问题Vector我认为,2016年仍在使用的任何图书馆都是非常可疑的。
dimo414 '16

@ dimo414这是一个IBM库,哈哈!(Lotus Domino数据API)。感谢您提供的信息,非常有帮助
user2808054 2016年

Answers:


122

这是不使用任何第三方库的简单实现。主要优势retainAllremoveAll并且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元素创建新列表,然后调用keepAll,addAll方法
lukastymo 2011年

为什么在此解决方案中使用strictfp?
lukastymo 2011年

9
应该使用HashSetfor,intersection以便平均案例性能为O(n)而不是O(n ^ 2)。
Zong

1
这篇文章可能会使用更新来演示Java 8 Stream API的好处。
SME_Dev

我尝试分配此值时遇到错误->示例: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代替。
科斯2012年

5
实际上,它是经过编辑的,请参见:“如果您接受重复,则使用List实现;如果您不接受,则使用Set实现:”
lukastymo 2013年

7
不,retainAll不是列表的交集。在上面,col中所有不在otherCol中的元素都被删除。假设otherCol是{a,b,b,c},而col是{b,b,b,c,d}。然后col以{b,b,b,c}结尾,严格来说,这不是两者的交集。我希望那是{b,b,c}。正在执行其他操作。
demongolem '16

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
糟糕,它可能是一个不错的单缸纸,但需要O(n ^ 2)时间。将列表之一转换为,Set然后使用集合的contains方法。并非生活中的所有事情都必须通过流来完成。
dimo414

31
list1.retainAll(list2) - is intersection

联盟将会成为removeAll现实addAll

在collection的文档中找到更多信息(ArrayList是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,2,3}。不应该是{1,2,3}作为交集吗?
GyuHyeon Choi

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)


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 + n),在O(n log n)中。合并以类似方式完成。只要确保您对if-elseif-else语句进行了适当的修改即可。

您还可以根据需要使用迭代器(我知道它们在C ++中效率更高,我也不知道在Java中是否也是如此)。


1
通用性不够,T可能不具有可比性,在某些情况下进行比较非常昂贵……
Boris Churzin

我不完全同意。比较贵吗?您将如何解决?
2013年

可悲的是-在O(n ^ 2)中这样做会更便宜:)对于数字,此解决方案很好...
Boris Churzin

可悲的是-您没有回答我的问题。让我重新表述一下,给定成本c(n)的比较函数,O(n ^ 2)会更好吗?
2016年

1
将一个输入转换为一组并contains()循环调用(如Devenv的建议)将花费O(n + m)时间。排序不必要地复杂,并且花费O(n log n + m log n + n)时间。可以减少到O(n log n)时间,但这仍然比线性时间差,而且复杂得多。
dimo414

4

我想Set如果要对它们进行交集和合并,则应使用a 来保存文件。然后,您可以使用GuavaSets类来进行操作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
  • keepAll将修改您的列表
  • 番石榴没有列表API(仅适用于set)

我发现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函数),则表之间最快的方法大约为1。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

我也在处理类似情况,并到达此处寻求帮助。最终找到了我自己的阵列解决方案。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
欢迎使用Stack Overflow!请注意,问题与ArrayList有关。另外,恐怕这个特定的实现会让事情变得不可取。输入中可能会出现值99999999(用作标记)。最好使用动态结构(例如ArrayList)来存储并集的结果。
SL Barth-恢复莫妮卡

1
请说明您提供的代码,而不只是代码答案。
tmarois

我只是提供一个线索,您必须输入任何垃圾值
Ashutosh

很高兴看到您添加了解释。不幸的是,答案本身仍然很糟糕。没有理由使用数组。您应该使用像ArrayList这样的动态结构。如果(由于某种原因)必须使用数组,则应考虑使用Integer而不是的数组int。然后,您可以使用null而不是“垃圾值”。“垃圾值”或“前哨值”通常不是一个好主意,因为这些值可能仍会出现在输入中。
SL Barth-恢复莫妮卡

0

经过测试,这是我最好的交叉点方法。

与纯HashSet方法相比,速度更快。下面的HashSet和HashMap对于具有超过100万条记录的数组具有类似的性能。

对于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

如果该数字与我正在检查的数字匹配,则该数字是否是首次出现,如果该数字是第一次匹配,则将其打印并保存到字符串中,以便在下一次相同的数字匹配时,将不会出现。 t打印,因为由于“ 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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.