集流的交集成新集

时间:2016-07-08 12:07:58

标签: java-8 java-stream

这个问题有更好,更简单的方法吗?

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

4 个答案:

答案 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);

这段代码只是一个例子。您可能/应该使用不同的方法转换为结果集。 EWAHCompressedBitmap32Iterable<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)

您可以通过检查foobar集合中的元素来过滤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());
    }
}