当两个元素共同时,合并设置

这是比较集的后续

我有

Set<Set> NestedSet = new HashSet<Set>(); [[Node[0], Node[1], Node[2]], [Node[0], Node[2], Node[6]], [Node[3], Node[4], Node[5]] [Node[2], Node[6], Node[7]] ] 

当有两个共同的元素时,我想合并集合。 例如,0,1,2和0,2,6有两个共同的元素,因此将它们合并为[0,1,2,6]。

[0,1,2,6]和[2,6,7]再次有2和6个共同点。 所以将它们合并并获得[0,1,2,6,7]。

最终输出应该是:

 [ [Node[0], Node[1], Node[2], Node[6], Node[7]], [Node[3], Node[4], Node[5]] ] 

我试过这样的:

  for (Set s1 : NestedSet ) { Optional<Set> findFirst = result.stream().filter(p -> { HashSet temp = new HashSet(s1); temp.retainAll(p); return temp.size() == 2; }).findFirst(); if (findFirst.isPresent()){ findFirst.get().addAll(s1); } else { result.add(s1); } } 

但我得到的结果是:

 [[Node[0], Node[1], Node[2], Node[6], Node[7]], [Node[3], Node[4], Node[5]], [Node[0], Node[2], Node[6], Node[7]]] 

任何的想法 ? 有没有办法获得所需的输出?

一些考虑:

  • 每次应用合并时,都必须重新启动该过程并迭代修改后的集合。 因此,输入集的迭代顺序很重要,如果您希望代码是确定性的,则可能需要使用在迭代顺序上提供保证的集合(例如,使用LinkedHashSet (不是HashSet )或List
  • 您当前的代码有副作用,因为它在合并时修改了提供的集合。 总的来说,我认为尽可能避免产生副作用是有帮助的。

以下代码执行您想要的操作:

 static  List> mergeSets(Collection> unmergedSets) { final List> mergedSets = new ArrayList<>(unmergedSets); List mergeCandidate = Collections.emptyList(); do { mergeCandidate = findMergeCandidate(mergedSets); // apply the merge if (!mergeCandidate.isEmpty()) { // gather the sets to merge final Set mergedSet = Sets.union( mergedSets.get(mergeCandidate.get(0)), mergedSets.get(mergeCandidate.get(1))); // removes both sets using their index, starts with the highest index mergedSets.remove(mergeCandidate.get(0).intValue()); mergedSets.remove(mergeCandidate.get(1).intValue()); // add the mergedSet mergedSets.add(mergedSet); } } while (!mergeCandidate.isEmpty()); return mergedSets; } // O(n^2/2) static  List findMergeCandidate(List> sets) { for (int i = 0; i < sets.size(); i++) { for (int j = i + 1; j < sets.size(); j++) { if (Sets.intersection(sets.get(i), sets.get(j)).size() == 2) { return Arrays.asList(j, i); } } } return Collections.emptyList(); } 

为了测试这个方法,我创建了两个辅助方法:

 static Set set(int... ints) { return new LinkedHashSet<>(Ints.asList(ints)); } @SafeVarargs static  Set> sets(Set... sets) { return new LinkedHashSet<>(Arrays.asList(sets)); } 

这些辅助方法允许编写非常易读的测试,例如(使用问题中的数字):

 public static void main(String[] args) { // prints [[2, 6, 7, 0, 1]] System.out.println(mergeSets(sets(set(0, 1, 2, 6), set(2, 6, 7)))); // prints [[3, 4, 5], [0, 2, 6, 1, 7]] System.out.println( mergeSets(sets(set(0, 1, 2), set(0, 2, 6), set(3, 4, 5), set(2, 6, 7)))); } 

我不确定你为什么会得到这个结果,但我确实看到了这个代码的另一个问题:它依赖于顺序。 例如,即使代码按预期工作, [Node[0], Node[1], Node[2]]首先与[Node[0], Node[2], Node[6]][Node[2], Node[6], Node[7]] 。 但是集合没有已定义的顺序,因此结果可能是非确定性的,也可能是依赖于实现的,具体取决于您的查看方式。

如果您真的想要确定性的依赖于顺序的操作,那么您应该使用List> ,而不是Set>