partitioningBy的目的是什么
What's the purpose of partitioningBy
例如,如果我打算对一些元素进行分区,我可以这样做:
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
的一个子案例。尽管前者接受 Predicate
作为参数而后者接受 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
?谢谢
partitioningBy
使用特殊的 Map
实现,优化了当键只是 boolean
.
时效率稍微高一些
(这也可能有助于阐明您的意思;partitioningBy
有助于有效地理解存在用于分区数据的布尔条件。)
partitioningBy
将始终 return 具有两个条目的映射,一个用于谓词为真,一个用于谓词为假。
两个条目可能都有空列表,但它们会存在。
这是 groupingBy
不会做的事情,因为它只在需要时创建条目。
在极端情况下,如果您向 partitioningBy
发送一个空流,您仍然会在地图中获得两个条目,而 groupingBy
将 return 一个空地图。
编辑:如下所述,Java 文档中未提及此行为,但是更改它会带走 partitioningBy
当前提供的附加值。对于 Java 9,这已经在规格中。
partitioningBy 方法将 return 一个映射,其键始终是布尔值,但在 groupingBy 方法的情况下,键可以是任何对象类型
//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的key始终是布尔值,groupingBy方式下key是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);
}
}
groupingBy
和partitioningBy
的另一个区别是前者取Function<? super T, ? extends K>
而后者取Predicate<? super T>
.
当你传递方法引用或lambda表达式时,例如s -> s.length() > 3
,它们可以被这两种方法中的任何一种使用(编译器会根据函数所需的类型推断函数接口类型您选择的方法)。
但是,如果您有 Predicate<T>
个实例,则只能将其传递给 Collectors.partitioningBy()
。它不会被 Collectors.groupingBy()
.
接受
同样,如果你有一个Function<T,Boolean>
实例,你只能将它传递给Collectors.groupingBy()
。它不会被 Collectors.partitioningBy()
.
接受
如其他答案所示,在某些情况下,将集合分成两组很有用。由于这两个分区将始终存在,因此更容易进一步利用它。在 JDK 中,为了隔离所有 class 文件和配置文件,使用了 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
例如,如果我打算对一些元素进行分区,我可以这样做:
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
的一个子案例。尽管前者接受 Predicate
作为参数而后者接受 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
?谢谢
partitioningBy
使用特殊的 Map
实现,优化了当键只是 boolean
.
(这也可能有助于阐明您的意思;partitioningBy
有助于有效地理解存在用于分区数据的布尔条件。)
partitioningBy
将始终 return 具有两个条目的映射,一个用于谓词为真,一个用于谓词为假。
两个条目可能都有空列表,但它们会存在。
这是 groupingBy
不会做的事情,因为它只在需要时创建条目。
在极端情况下,如果您向 partitioningBy
发送一个空流,您仍然会在地图中获得两个条目,而 groupingBy
将 return 一个空地图。
编辑:如下所述,Java 文档中未提及此行为,但是更改它会带走 partitioningBy
当前提供的附加值。对于 Java 9,这已经在规格中。
partitioningBy 方法将 return 一个映射,其键始终是布尔值,但在 groupingBy 方法的情况下,键可以是任何对象类型
//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的key始终是布尔值,groupingBy方式下key是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);
}
}
groupingBy
和partitioningBy
的另一个区别是前者取Function<? super T, ? extends K>
而后者取Predicate<? super T>
.
当你传递方法引用或lambda表达式时,例如s -> s.length() > 3
,它们可以被这两种方法中的任何一种使用(编译器会根据函数所需的类型推断函数接口类型您选择的方法)。
但是,如果您有 Predicate<T>
个实例,则只能将其传递给 Collectors.partitioningBy()
。它不会被 Collectors.groupingBy()
.
同样,如果你有一个Function<T,Boolean>
实例,你只能将它传递给Collectors.groupingBy()
。它不会被 Collectors.partitioningBy()
.
如其他答案所示,在某些情况下,将集合分成两组很有用。由于这两个分区将始终存在,因此更容易进一步利用它。在 JDK 中,为了隔离所有 class 文件和配置文件,使用了 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