Java中的列表差异

46

我有两个 ArrayList<Integer>,内容如下:

原始列表: 12, 16, 17, 19, 101

选中列表: 16, 19, 107, 108, 109

我想对这两个列表进行差异处理,最终得到两个列表:

添加: 108, 109, 107

删除: 12, 17, 101

原始列表和选中列表的长度可能不同,其中一个可能比另一个长或短。


所以基本上你想要消除出现在两个列表中的值?这些列表是否总是按照你的示例排序? - Asaph
3
“Add: 108,109” 应该翻译为 “添加:108,109”,而问题的意思是是否应该将其改为“添加:107,108,109”? - Asaph
3
我不明白这两个集合是交集还是并集。这两个集合的交集应该是16,19 - Mark Peters
@Asaph 是的,应该是这样的。我的错误。 - learn_plsql
7
你不是在要求交集 - 修改标题。 - Radu Simionescu
标题有误导性,您要求的是集合差,而不是交集或并集。 - Ahmad Shahwan
11个回答

52

作为替代方案,您可以使用来自Apache commons库的CollectionUtils。它具有适用于您情况的静态intersectionunionsubtract方法。


在我看来,这应该是最佳答案。谢谢 :) - Tohid
很遗憾,它不支持Java泛型 :( - rafalmag
1
它在版本4中实现:https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/CollectionUtils.html#intersection(java.lang.Iterable, java.lang.Iterable) - user829755
1
ListUtils.removeAll 运行得非常完美,我不喜欢 JDK 的 removeAll,因为它会修改原始列表,而我必须从要删除的列表中定义一个新列表来命名差异。 - zwx

43
List<Integer> original = Arrays.asList(12,16,17,19,101);
List<Integer> selected = Arrays.asList(16,19,107,108,109);

ArrayList<Integer> add = new ArrayList<Integer>(selected);
add.removeAll(original);
System.out.println("Add: " + add);

ArrayList<Integer> remove = new ArrayList<Integer>(original);
remove.removeAll(selected);
System.out.println("Remove: " + remove);

输出:

Add: [107, 108, 109]
Remove: [12, 17, 101]

使用Collection的removeAll方法。请参见javadocs。


2
请注意,removeAll会删除每个值的所有出现次数 - 因此,如果'16'在selected中出现两次,它仍然不会出现在最终的add中。因此,如果您需要检测重复项(例如在列表中查找缺失、意外和重复项),则不适用。 - simon.watts

24

交集: original.retainAll(selected)

执行后,原始集合将只包含两个集合中都存在的元素。如果有任何更改,则返回true。

警告:对于大型集合,此方法非常缓慢。


1
是的,但它可能会重复元素,因此它不严格是一个交集。 - kike
1
@kike,如果你不想重复元素,请使用Set https://docs.oracle.com/javase/7/docs/api/java/util/Set.html#retainAll(java.util.Collection)。 - ACV

9

对于交集和并集操作,自然的集合类型是Set而不是List,使用Set更加高效。


6
使用Guava库:
List<Integer> listA = Lists.newArrayList(12,16,17,19,101);
List<Integer> listB = Lists.newArrayList(16,19,107,108,109);
Set<Integer> intersection = Sets.intersection(Sets.newHashSet(listA), Sets.newHashSet(listB));
listA.removeAll(intersection);
listB.removeAll(intersection);

1
Guavas Sets.intersection存在错误风险,因为第二个参数是一个Set<?>。你可以传递例如Set<String>,编译器不会抱怨。 - user829755

5
 List<Integer> original;
 List<Integer> selected;

 List<Integer> add = new ArrayList<Integer>(selected);
 add.removeAll(original);

 List<Integer> remove = new ArrayList<Integer>(original);
 remove.removeAll(selected);

在处理重复元素的边界情况时要小心。 应该尊重基数吗? 例如,如果我最初有5,6,之后是5,5,6,那么加法应该是5吗? 使用Set更好,因为它们没有重复项(而且contains()查找速度更快,因为它们按其包含的数据进行索引)。


2

如果您想获取列表的交集,可以使用此方法。

List<Address> resultsIntersectionSet( List<Set<Address>> inputListOfLists )
{
    Set<Address> intersection = new HashSet<>();

    if ( !inputListOfLists.isEmpty() )
        intersection = inputListOfLists.get( 0 );

    for ( Set<Address> filterResultList : inputListOfLists )
    {
        intersection.retainAll( filterResultList );
    }

    return new ArrayList<>( intersection );
}

1

现在有一个新的库可用underscore-java。它可以对列表和数组进行差异和交集操作。实时示例。

代码示例:

List<Integer> original = Arrays.asList(12, 16, 17, 19, 101);
List<Integer> selected = Arrays.asList(16, 19, 107, 108, 109);
List<Integer> add = U.difference(selected, U.intersection(original, selected));
List<Integer> remove = U.difference(original, selected);

1
package LAB8Pack;
import java.util.HashSet; 
import java.util.Iterator;
import java.lang.StringBuilder; 


public class HashSetDemo {

    public static void main(String[] args) {
        HashSet<String> round = new HashSet<String> (); 
        HashSet<String> green = new HashSet<String> (); 


        // Add elements to 'round' and 'green' sets 
        //----------------------------------------------------
        round.add("peas"); 
        green.add("peas");
        round.add("watermelon"); 
        green.add("watermelon");
        round.add("basketball"); 
        green.add("chameleon");
        round.add("chameleon"); 
        green.add("grass");
        round.add("eyes"); 
        green.add("book");

        // Create 'setUnion' and 'setInter'  
        // ---------------------------------------------------
        HashSet<String> setUnion = new HashSet<String>();   

        // Use this to find the intersection
        HashSet<String> SETINTER = new HashSet<String>();   


        HashSet<String> setInter1 = new HashSet<String>(round);
        HashSet<String> setInter2 = new HashSet<String>(green);



        // Add all the elements to one set
        setUnion.addAll(round);
        setUnion.addAll(green);
        SETINTER.addAll(setUnion);



        // Create an intersection
        setInter1.removeAll(green); // Get unique items in round
        setInter2.removeAll(round); // Get unique items in green
        SETINTER.removeAll(setInter2); // Remove items that are unique to green
        SETINTER.removeAll(setInter1); // Remove items that are unique to round
        //----------------------------------------------------


        // DISPLAY RESULTS 
        // ===================================================
        System.out.println("Content of set round"); 
        System.out.println("-----------------------");
        System.out.println(OutputSet(round));

        System.out.println("Content of set green");
        System.out.println("-----------------------");
        System.out.println(OutputSet(green)); 

        System.out.println("Content of set Union");
        System.out.println("-----------------------");
        System.out.println(OutputSet(setUnion));



        System.out.println("Content of set Intersection"); 
        System.out.println("-----------------------");
        System.out.println(OutputSet(SETINTER));
    }




    // METHODS 
    // =======================================================
    static StringBuilder OutputSet (HashSet<String> args) {
        Iterator iterator = args.iterator();
        StringBuilder sB = new StringBuilder (); 

        while (iterator.hasNext()) {
            sB.append(iterator.next() + " \n");
        }
        return sB; 
    }  
}

0
这是一个用于查找多个集合(超过2个)交集的函数 -
public static <T, C extends Collection<T>> C findIntersection(C newCollection,
                                                            Collection<T>... collections) {
  boolean first = true;
  for (Collection<T> collection : collections) {
      if (first) {
          newCollection.addAll(collection);
          first = false;
      } else {
          newCollection.retainAll(collection);
      }
  }
  return newCollection;
}

使用方法 -

public static void main(String[] args) {
  List<Integer> l1 = List.of(1, 3, 5, 7, 9, 11, 13);
  List<Integer> l2 = List.of(1, 2, 3, 5, 8, 13);
  List<Integer> l3 = List.of(2, 3, 5, 7, 11, 13);
  Set<Integer> intersection = findIntersection(new HashSet<>(), l1, l2, l3);
  System.out.println(intersection);
 }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接