【问题标题】:How can I get a list containing the differences between two sets?如何获得包含两组之间差异的列表?
【发布时间】:2022-02-03 07:24:51
【问题描述】:

假设我有两套:

Set<String> set1 = new HashSet<>(Arrays.asList("a", "b", "c", "d", "e") );
Set<String> set2 = new HashSet<>(Arrays.asList("b", "c", "d", "e", "f") );

在性能方面,比较两者并获得List 的差异的最简单和最好的方法是什么? 这意味着我应该得到一个包含"a""f" 的列表。这里的棘手之处在于,差异可能出现在任一列表中。

我只能让它与 for 循环一起工作,但必须有更简单的方法......

【问题讨论】:

  • 嗯,更简单的方法可能是使用库,例如Apache Commons Collection 的CollectionUtils.disjunction()。但是无论如何,自己做都不应该那么困难或复杂,例如如果性能不是那么重要,请尝试创建每个集合的副本,在每个集合上调用 removeAll() 并传递另一个原始集合,最后组合 2,例如copyOfS1.removeAll(set2); copyOfS2.removeAll(set1); copyOfS1.addAll(copyOfS2);.
  • 这能回答你的问题吗? Union or intersection of Java Sets
  • @fantaghirocco 我不想做联合或交叉,我实际上是在尝试做与交叉相反的事情。交集返回两个列表中包含的所有元素,这不是我想要的。
  • @Thomas 性能至关重要,这就是问题所在。
  • 好吧,你应该从一开始就这么说。你只是要求“更容易”。

标签: java list set compare


【解决方案1】:

使用流可以通过Collectors.groupingByCollectors.counting() 来确定出现次数,然后过滤任何出现多次的字符串:

List<String> differences = Stream.concat(set1.stream(), set2.stream())
                .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))  // Map<String, Long> -> key -> element, value -> count of occurrences
                .entrySet().stream()
                .filter(e -> e.getValue() == 1) // filter not unique elements from 2 sets
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());

【讨论】:

  • 好吧,这是有道理的。如果我将其设为Set 而不是List,这是否也有效,因为 Set 永远不能包含重复项?
  • @Ben 是的,它会起作用,然后使用Collectors.toSet()
【解决方案2】:

执行此操作的一种方法是创建一组集合,遍历另一个集合,并在删除该元素时检查该元素是否包含:

Collection<String> c1 = Arrays.asList("a", "b", "c", "d", "e");
Collection<String> c2 = Arrays.asList("b", "c", "d", "e", "f");

Set<String> set = new HashSet<>(c1); //one loop over c1 to create the set - O(n)
List<String> differences = new LinkedList<>();
    
//one loop over c2 - O(m)
for( String e : c2 ) {
   boolean removed = set.remove(e); //should be O(1) due to hashset
   //if the element wasn't removed it was not in the set and thus not in c1
   if( !removed ) { 
       differences.add(e); //should be O(1) due to linked list
   }
}
    
//at this point set only contains elements not in c2 as those have been removed by the loop
differences.addAll(set); //at most O(n) if nothing got removed

如您所见,我们有 2 个 O(n) 和 1 个 O(m) 操作,因此总时间复杂度为 O(n + m)。

【讨论】:

    【解决方案3】:

    这样的?

        public static void main(String[] args) {
            
            Set<String> set1 = new HashSet<>(Arrays.asList("a", "b", "c", "d", "e") );
            Set<String> set2 = new HashSet<>(Arrays.asList("b", "c", "d", "e", "f") );
            //get intersection array
            Set<String> listSame =  new HashSet<>(set2);
            listSame.retainAll(set1);
            System.out.println(listSame);
            //get union array
            Set<String> listDiff =  new HashSet<>(set1);
            listDiff.addAll(set2);
            // get difference
            listDiff.removeAll(listSame);
            System.out.println(listDiff);
        }
    

    最好的方法可能是使用联合和交叉点,然后获得差异。

    【讨论】:

    • 您确实意识到由于Set&lt;String&gt; listSame = set2; 操作listSame.retainAll(set1); 也会改变set2,不是吗?因此 `listDiff.addAll(set2);` 和 listDiff.removeAll(listSame); 没有多大意义 - 您首先添加 set2 中的所有元素,然后删除它们(这也会删除那些已经存在于 set1 中的元素。
    • 我的错,我只检查了输出。我现在会调整它。
    【解决方案4】:

    我回复晚了,但认为代码更清晰易懂。

    在这里您可以看到如何使用 Java 集进行联合和交集:https://stackoverflow.com/a/51113135/4222206

    回到你的问题,看看两组的区别:

    • 创建并集和交集
    • 从并集中减去交集

    在代码中表示:

      Set union = new Set(set1);
      union.addAll(set2);
      Set intersection = new Set(set1);
      intersection.retainAll(set2);
      
      Set result = new Set(union);
      result.removeAll(intersection);
    

    现在结果你得到了两个集合没有共同点的所有东西,它应该包含 [a, f]。

    【讨论】:

      【解决方案5】:

      请按照我发送的屏幕截图进行操作,我认为这是使用 java 8 流的最简单方法

      【讨论】:

      猜你喜欢
      • 2020-10-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-12
      • 1970-01-01
      • 1970-01-01
      • 2022-11-24
      • 2022-07-23
      相关资源
      最近更新 更多