【问题标题】:Intersection and union of ArrayLists in JavaJava中ArrayList的交集和并集
【发布时间】:2011-07-14 01:45:48
【问题描述】:

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

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

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

【问题讨论】:

  • 如果您不想创建一个新列表,Vector.retainAll(Vector) 会将您的原始向量修剪为仅与第二个向量的交集。
  • @user2808054 为什么是Vector?从 Java 1.2 开始不鼓励使用该类。
  • @dimo414 我正在使用的接口(我没有选择)将事物作为向量返回。我不知道它已经气馁了!感谢您的信息.. 被谁气馁?我没有看到任何关于它被弃用的说明,所以这是一个惊喜
  • 来自 Javadocs:“As of the Java 2 platform v1.2 ... it is recommended to use ArrayList in place of Vector.”。您可能需要Vector的唯一一次是用于跨线程交互,但对于这些用例也有更安全的数据结构。另见this question。在我看来,任何在 2016 年仍在使用 Vector 的图书馆都非常可疑。
  • @dimo414 这是一个 IBM 库,哈哈! (Lotus Domino 数据 API)。感谢您的信息,非常有帮助

标签: java list union intersection


【解决方案1】:

您可以从apache commons 使用CollectionUtils

【讨论】:

  • 如果有人觉得这个答案有点太短:'CollectionUtils.containsAny' 和 'CollectionUtils.containsAll' 是方法。
  • 很奇怪,来自 apache commons 的 CollectionUtils 不支持泛型
【解决方案2】:
list1.retainAll(list2) - is intersection

联合将是removeAll,然后是addAll

在集合的文档中找到更多(ArrayList 是一个集合) http://download.oracle.com/javase/1.5.0/docs/api/java/util/Collection.html

【讨论】:

  • retainAll()removeAll() 都是对列表的 O(n^2) 操作。我们可以做得更好。
  • 我投了赞成票,但现在我有一个问题。 retainAll 的 {1, 2, 2, 3, 4, 5} 超过 {1, 2, 3} 导致 {1, 2, 2, 3}。不应该是 {1, 2, 3} 作为交叉点吗?
  • @ghchoi list 和 set 背后的语义现在是问题所在。使用列表 [1, 2, 2, 3, 4, 5] 我们接受重复但对于集合 {1, 2, 3} 不允许重复。另外两种表示法通常不同但不固定,对于列表 [...重复是一个特征...] 和对于集合 {...不允许重复...}
【解决方案3】:

Collection(所以 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]

【讨论】:

  • 有人建议编辑此联合“不正确,因为它将包含两次公共元素”。编辑建议改用HashSet
  • 其实已经编辑过了,见:“如果接受重复就用List实现,不接受就用Set实现:”
  • 不,retainAll 不是列表的交集。在上面,col 中所有不在 otherCol 中的元素都被删除。假设 otherCol 是 {a,b,b,c} 而 col 是 {b,b,b,c,d}。然后 col 以 {b,b,b,c} 结束,这不是严格意义上的两者的交集。我希望那是{b,b,c}。正在执行不同的操作。
  • 我也看不出addAll() 是列表的联合;它只是将第二个列表连接到第一个列表的末尾。如果第一个列表已包含元素,则联合操作将避免添加元素。
【解决方案4】:

如果您将数据放在 Sets 中,则可以使用 Guava 的 Sets 类。

【讨论】:

    【解决方案5】:

    如果你想对它们进行交集和联合,我认为你应该使用Set 来保存文件。然后您可以使用GuavaSets 类来执行unionintersection 并通过Predicate 进行过滤。这些方法与其他建议的区别在于,所有这些方法都创建了两个集合的并集、交集等的惰性视图。 Apache Commons 创建一个新集合并将数据复制到其中。 retainAll 通过从中删除元素来更改您的集合之一。

    【讨论】:

      【解决方案6】:

      仅为集合而非列表定义的联合和交集。正如你所提到的。

      检查guava 库中的过滤器。番石榴也提供真实的intersections and unions

       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】:

        这是一个没有使用任何第三方库的简单实现。与retainAllremoveAlladdAll 相比,主要优势在于这些方法不会修改输入到方法的原始列表。

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

        【讨论】:

        • 你可以用list1元素创建新列表,然后调用retainAll、addAll方法
        • 为什么在这个解决方案中使用 strictfp?
        • 应该为intersection 使用HashSet,这样平均情况下的性能是O(n) 而不是O(n^2)。
        • 这篇文章可以使用更新来展示 Java 8 Stream API 的好处。
        • 当我尝试分配此值时出现错误 -> 示例:ArrayList total total = (ArrayList) intersection(list2, list1) --->无法转换 java.util。 arraylist 到 java.util.arraylist
        【解决方案8】:

        标记的解决方案无效。它的时间复杂度为 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 中是否也是如此)。

        【讨论】:

        • 不够通用,T 可能无法比较,而且在某些情况下比较成本很高......
        • 不是通用的,我完全同意。比较贵?你会怎么解决呢?
        • 可悲的是 - 在 O(n^2) 中这样做会更便宜 :) 对于数字,这个解决方案很好......
        • 很遗憾 - 你没有回答我的问题。让我换个说法,给定成本 c(n) 的比较函数,O(n^2) 如何更好?
        • 将一个输入转换为一组并在循环中调用contains()(正如 Devenv 所建议的那样)将花费 O(n + m) 时间。排序是不必要的复杂,需要 O(n log n + m log n + n) 时间。当然,这会减少到 O(n log n) 时间,但这仍然比线性时间差,而且要复杂得多。
        【解决方案9】:

        这是一种与流进行交集的方法(请记住,您必须使用 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-object,那么您可以修改您的流:

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

        【讨论】:

          【解决方案10】:
          • retainAll 将修改您的列表
          • Guava 没有 List 的 API(仅用于 set)

          我发现 ListUtils 对于这个用例非常有用。

          如果您不想修改现有列表,请使用 org.apache.commons.collections 中的 ListUtils。

          ListUtils.intersection(list1, list2)

          【讨论】:

            【解决方案11】:

            这篇文章相当老了,但它是第一个在谷歌上搜索该主题时弹出的帖子。

            我想在一行中使用 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());
            

            如果有人有更好/更快的解决方案,请告诉我,但这个解决方案是一个很好的单行程序,可以很容易地包含在方法中,而无需添加不必要的帮助类/方法,并且仍然保持可读性。

            【讨论】:

            • 哎呀,这可能是一个不错的单行,但它需要 O(n^2) 时间。将其中一个列表转换为Set,然后使用集合的contains 方法。并非生活中的所有事情都必须通过流来完成。
            【解决方案12】:

            如果列表中的对象是可散列的(即有一个不错的 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;
                }
            }
            

            【讨论】:

              【解决方案13】:

              最终解决方案:

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

              【讨论】:

              • 第一种方法不变异,后两种变异,感觉不一致。将list1.retainAll(list2); 包裹在getListIntersection 中有什么意义?它已经是一行,它可能会隐藏它正在改变第一个列表,因为它返回一个列表
              【解决方案14】:

              我也在处理类似的情况,并来到这里寻求帮助。最终找到了我自己的数组解决方案。 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 );
                      }
              

              【讨论】:

                【解决方案15】:

                如果数字匹配而不是我检查它是否第一次出现,如果数字第一次匹配,则在“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;
                                }
                           }
                       }    
                }
                

                }

                【讨论】:

                • 不要只是发布代码作为答案,对你在做什么做一些简单的解释
                • 这是我上传的第一个程序
                • 虽然这段代码可能有助于解决问题,但它并没有解释为什么和/或如何回答问题。提供这种额外的背景将显着提高其长期价值。请edit您的答案添加解释,包括适用的限制和假设。
                【解决方案16】:

                首先,我将数组的所有值复制到一个数组中,然后将重复值删除到数组中。第 12 行,解释如果相同的数字出现的时间超过时间,则将一些额外的垃圾值放入“j”位置。最后从start-end遍历,检查是否有相同的垃圾值,然后丢弃。

                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]+" ");
                    }
                }   
                }
                

                【讨论】:

                • 欢迎来到 Stack Overflow!请注意,问题是关于 ArrayList。另外,我担心这种特殊的实现会留下一些不足之处。用作标记的值 99999999 可能出现在输入中。最好使用动态结构,比如ArrayList,来存储联合的结果。
                • 请解释您提供的代码,而不仅仅是代码答案。
                • 我只是给你一个线索,你必须把任何垃圾值
                • 很高兴看到您添加了解释。不幸的是,答案本身仍然很糟糕。没有理由使用数组。您应该使用像 ArrayList 这样的动态结构。如果(出于某种原因)您必须使用数组,您应该考虑使用Integer 的数组而不是int。然后你可以使用null 而不是你的“垃圾值”。 “垃圾值”或“标记值”通常不是一个好主意,因为这些值可能仍会出现在输入中。
                【解决方案17】:

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

                【讨论】:

                  【解决方案18】:

                  您可以使用 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]
                  

                  【讨论】:

                    【解决方案19】:

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

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

                    【讨论】:

                      【解决方案20】:

                      基于公共键的不同对象的两个列表的交集 - 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());
                          }
                      

                      【讨论】:

                      • 这两个列表之间的差异如何?
                      【解决方案21】:
                      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());
                      

                      【讨论】:

                        【解决方案22】:

                        retainAll() 方法用于查找公共元素..即;交集 list1.retainAll(list2)

                        【讨论】:

                          【解决方案23】:

                          自 Java 8 以来的单行代码

                          导入静态 java.util.stream.Stream.concat;
                          导入静态 java.util.stream.Collectors.toList;
                          导入静态 java.util.stream.Collectors.toSet;

                          如果没有重复则联合:

                            return concat(a.stream(), b.stream()).collect(toList());
                          

                          联合和不同:

                            return concat(a.stream(), b.stream()).distinct().collect(toList());
                          

                          如果 Collection/Set 返回类型,则联合和不同:

                            return concat(a.stream(), b.stream()).collect(toSet());
                          

                          如果没有重复则相交:

                            return a.stream().filter(b::contains).collect(toList());
                          

                          如果集合 b 很大而不是 O(1),则通过在 return 之前添加 1 行来预优化过滤器性能。复制到HasSet(import java.util.Set;)

                          ... b = Set.copyOf(b);

                          相交和不同:

                            return a.stream().distinct().filter(b::contains).collect(toList());
                          

                          【讨论】:

                            【解决方案24】:

                            您可以使用以下方法:

                            CollectionUtils.containsAnyCollectionUtils.containsAll

                            来自Apache Commons

                            【讨论】:

                              猜你喜欢
                              • 1970-01-01
                              • 2018-12-09
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 1970-01-01
                              • 2015-07-16
                              相关资源
                              最近更新 更多