这个问题有更好,更简单的方法吗?
@Test
public void testReduce() {
Set<Integer> foo = ImmutableSet.of(1,2,3,4,8,9);
Set<Integer> bar = ImmutableSet.of(1,3,8,5,11);
//DO think about solution for 1..n sets, and not only two.
Set<Integer> intersection = ImmutableList.of(foo,bar)
.stream()
.reduce( null, (a, b) -> {
if ( a == null ) {
a = new HashSet<Integer>(b);
}
else {
a.retainAll(b);
}
return a;
});
assertThat( intersection, is( ImmutableSet.of( 1,3,8) ) );
}
答案 0 :(得分:8)
reduce
是错误的方法,因为不允许以这种方式修改函数的参数。这是可变缩减,也称为collect
:
List<Set<Integer>> listOfSets=…;
//check if at least one set is in the list
Set<Integer> intersection = listOfSets.stream().skip(1)
.collect(()->new HashSet<>(listOfSets.get(0)), Set::retainAll, Set::retainAll);
必须先查看第一个集合,这里是一个缺点,但是使用null
作为标识值也不是干净的(并且不能与collect
一起使用,因为累加器无法返回一套新的。)
答案 1 :(得分:3)
如果您使用Eclipse Collections:
,以下内容将有效@Test
public void testReduce()
{
ImmutableSet<Integer> foo = Sets.immutable.of(1, 2, 3, 4, 8, 9);
ImmutableSet<Integer> bar = Sets.immutable.of(1, 3, 8, 5, 11);
// Works with Eclipse Collections 7.0 or above
ImmutableSet<Integer> intersection1 = Lists.mutable.of(foo, bar)
.stream()
.reduce(ImmutableSet::intersect).get();
Assert.assertEquals(intersection1, Sets.immutable.of(1, 3, 8));
// Works with Eclipse Collections 8.0.0-M1 or above
ImmutableSet<Integer> intersection2 = Lists.immutable.of(foo, bar)
.reduce(ImmutableSet::intersect).get();
Assert.assertEquals(intersection2, Sets.immutable.of(1, 3, 8));
}
这也适用于MutableSet
。
@Test
public void testReduce()
{
MutableSet<Integer> foo = Sets.mutable.of(1, 2, 3, 4, 8, 9);
MutableSet<Integer> bar = Sets.mutable.of(1, 3, 8, 5, 11);
// Works with Eclipse Collections 7.0 or above
MutableSet<Integer> intersection1 = Lists.mutable.of(foo, bar)
.stream()
.reduce(MutableSet::intersect).get();
Assert.assertEquals(intersection1, Sets.immutable.of(1, 3, 8));
// Works with Eclipse Collections 8.0.0-M1 or above
MutableSet<Integer> intersection2 = Lists.immutable.of(foo, bar)
.reduce(MutableSet::intersect).get();
Assert.assertEquals(intersection2, Sets.immutable.of(1, 3, 8));
}
在Eclipse集合中,ImmutableSet
不会扩展java.util.Set
,因为Set
是一个可变接口。 MutableSet
确实扩展了java.util.Set
。在question的答案中解释了这种设计选择。
注意:我是Eclipse Collections的提交者。
答案 2 :(得分:1)
此答案仅适用于整数集,而不适用于通用集。但对于寻求速度的人来说,有时整数列表是压缩位图的一个很好的例子。你应该检查一下你的整数是否组合得好,在某些情况下,如果你这样做,你可以获得几个数量级的速度(使用com.googlecode.javaewah32,Apache 2.0许可证):
Set<Integer> foo = ImmutableSet.of(1,2,3,4,8,9);
Set<Integer> bar = ImmutableSet.of(1,3,8,5,11);
EWAHCompressedBitmap32 fooBitmap = new EWAHCompressedBitmap32();
EWAHCompressedBitmap32 barBitmap = new EWAHCompressedBitmap32();
//fill bitmaps
foo.stream().forEach(fooBitmap::set);
bar.stream().forEach(barBitmap::set);
//fooBitmap.and(barBitmap) returns intersection of sets now. fast!
ImmutableSet<Integer> intersection = ImmutableSet.<Integer>builder()
.addAll(fooBitmap.and(barBitmap))
.build();
System.out.println(intersection);
这段代码只是一个例子。您可能/应该使用不同的方法转换为结果集。 EWAHCompressedBitmap32
是Iterable<Integer>
所以,想象力没有限制。
现在,上面的代码只交叉了2套。要交叉列表中的所有集合,您可以执行通常的reduce:
Set<Integer> foo = ImmutableSet.of(1,2,3,4,8,9);
Set<Integer> bar = ImmutableSet.of(1,3,8,5,11);
List<Set<Integer>> sets = ImmutableList.of(foo,bar);
EWAHCompressedBitmap32 res = sets.stream().map(l -> {
EWAHCompressedBitmap32 b = new EWAHCompressedBitmap32();
l.stream().forEach(b::set);
return b;
}).reduce(null, (l, r) -> l == null ? r : l.and(r));
System.out.println(res);
另一种选择是使用还原收集器:
EWAHCompressedBitmap32 res = sets.stream().collect(Collectors.reducing(
//identity
null,
//mapper set -> compressedBitmap
l -> {
EWAHCompressedBitmap32 b = new EWAHCompressedBitmap32();
l.stream().forEach(b::set);
return b;
},
//and-reducer
(l, r) -> l == null ? r : l.and(r)
));
答案 3 :(得分:0)
您可以通过检查foo
或bar
集合中的元素来过滤otherBar
的元素:
Set<Integer> set =
foo.stream().filter(e -> Stream.of(bar, otherBar).allMatch(s -> s.contains(e))).collect(toSet());
例如,如果您收到Collection<Set<T>>
,则说明:
public static <T> Set<T> intersection(Collection<Set<T>> input) {
if(input.isEmpty()) {
return Collections.emptySet();
} else {
Set<T> first = input.iterator().next();
//if the collection allows to remove element, you can remove first before
return first.stream().filter(e -> input.stream().allMatch(s -> s.contains(e))).collect(toSet());
}
}