51

例如,如果我打算对一些元素进行分区,我可以执行以下操作:

Stream.of("I", "Love", "Stack Overflow")
      .collect(Collectors.partitioningBy(s -> s.length() > 3))
      .forEach((k, v) -> System.out.println(k + " => " + v));

输出:

false => [I]
true => [Love, Stack Overflow]

但对我partioningBy来说只是groupingBy. 尽管前者接受 aPredicate作为参数,而后者接受 a Function,但我只是将分区视为正​​常的分组函数。

所以同样的代码做同样的事情:

 Stream.of("I", "Love", "Stack Overflow")
       .collect(Collectors.groupingBy(s -> s.length() > 3))
       .forEach((k, v) -> System.out.println(k + " => " + v));

这也导致Map<Boolean, List<String>>.

那么有什么理由我应该使用partioningBy而不是groupingBy?谢谢

4

5 回答 5

64

partitioningBy将始终返回一个包含两个条目的映射,一个用于谓词为真,另一个用于谓词为假。两个条目可能都有空列表,但它们会存在。

这是groupingBy不会做的事情,因为它只在需要时创建条目。

在极端情况下,如果您向您发送一个空流,partitioningBy您仍然会在映射中获得两个条目,而groupingBy将返回一个空映射。

编辑:正如下面提到的,Java 文档中没有提到这种行为,但是改变它会带走partitioningBy当前提供的附加值。对于 Java 9,这已经在规范中。

于 2016-10-27T21:55:13.743 回答
19

partitioningBy稍微高效一点,使用Map针对键只是 a 时优化的特殊实现boolean

(这也可能有助于澄清你的意思;partitioningBy有助于有效地理解存在用于分区数据的布尔条件。)

于 2015-01-16T22:17:12.373 回答
4

partitioningBy方法将返回一个映射,其键始终是布尔值,但在groupingBy方法的情况下,键可以是任何 Object 类型

//groupingBy
Map<Object, List<Person>> list2 = new HashMap<Object, List<Person>>();
list2 = list.stream().collect(Collectors.groupingBy(p->p.getAge()==22));
System.out.println("grouping by age -> " + list2);

//partitioningBy
Map<Boolean, List<Person>> list3 = new HashMap<Boolean, List<Person>>();
list3 = list.stream().collect(Collectors.partitioningBy(p->p.getAge()==22));
System.out.println("partitioning by age -> " + list2);

如您所见,在 partitioningBy 方法的情况下,map 的键始终是布尔值,但在 groupingBy 方法的情况下,键是 Object 类型

详细代码如下:

    class Person {
    String name;
    int age;

    Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public String toString() {
        return this.name;
    }
}

public class CollectorAndCollectPrac {
    public static void main(String[] args) {
        Person p1 = new Person("Kosa", 21);
        Person p2 = new Person("Saosa", 21);
        Person p3 = new Person("Tiuosa", 22);
        Person p4 = new Person("Komani", 22);
        Person p5 = new Person("Kannin", 25);
        Person p6 = new Person("Kannin", 25);
        Person p7 = new Person("Tiuosa", 22);
        ArrayList<Person> list = new ArrayList<>();
        list.add(p1);
        list.add(p2);
        list.add(p3);
        list.add(p4);
        list.add(p5);
        list.add(p6);
        list.add(p7);

        // groupingBy
        Map<Object, List<Person>> list2 = new HashMap<Object, List<Person>>();
        list2 = list.stream().collect(Collectors.groupingBy(p -> p.getAge() == 22));
        System.out.println("grouping by age -> " + list2);

        // partitioningBy
        Map<Boolean, List<Person>> list3 = new HashMap<Boolean, List<Person>>();
        list3 = list.stream().collect(Collectors.partitioningBy(p -> p.getAge() == 22));
        System.out.println("partitioning by age -> " + list2);

    }
}
于 2017-09-16T06:03:54.283 回答
3

groupingByand之间的另一个区别partitioningBy是前者采用 aFunction<? super T, ? extends K>而后者采用 a Predicate<? super T>

当您传递方法引用或 lambda 表达式时,例如s -> s.length() > 3,它们可以被这两种方法中的任何一种使用(编译器将根据您选择的方法所需的类型推断函数接口类型)。

但是,如果您有Predicate<T>实例,则只能将其传递给Collectors.partitioningBy(). 它不会被Collectors.groupingBy().

同样,如果您有Function<T,Boolean>实例,则只能将其传递给Collectors.groupingBy(). 它不会被Collectors.partitioningBy().

于 2018-05-31T08:15:39.393 回答
0

如其他答案所示,将集合分为两组在某些情况下很有用。由于这两个分区始终存在,因此进一步利用它会更容易。在 JDK 中,使用隔离所有类文件和配置文件partitioningBy

    private static final String SERVICES_PREFIX = "META-INF/services/";
    
    // scan the names of the entries in the JAR file
    Map<Boolean, Set<String>> map = jf.versionedStream()
            .filter(e -> !e.isDirectory())
            .map(JarEntry::getName)
            .filter(e -> (e.endsWith(".class") ^ e.startsWith(SERVICES_PREFIX)))
            .collect(Collectors.partitioningBy(e -> e.startsWith(SERVICES_PREFIX),
                                               Collectors.toSet()));

    Set<String> classFiles = map.get(Boolean.FALSE);
    Set<String> configFiles = map.get(Boolean.TRUE);

代码片段来自jdk.internal.module.ModulePath#deriveModuleDescriptor

于 2020-07-31T11:06:09.350 回答