collect 高阶操作
聚合
public class StreamTest {
private static List<Person> list;
static {
list = new ArrayList<>();
list.add(new Person("i", 18, "杭州", 999.9));
list.add(new Person("am", 19, "温州", 777.7));
list.add(new Person("iron", 21, "杭州", 888.8));
list.add(new Person("man", 17, "宁波", 888.8));
}
/**
* 演示用collect()方法实现聚合操作,对标max()、min()、count()
* @param args
*/
public static void main(String[] args) {
// 方式1:匿名对象
Optional<Person> max1 = list.stream().collect(Collectors.maxBy(new Comparator<Person>() {
@Override
public int compare(Person p1, Person p2) {
return p1.getAge() - p2.getAge();
}
}));
System.out.println(max1.orElse(null));
// 方式2:Lambda
Optional<Person> max2 = list.stream().collect(Collectors.maxBy((p1, p2) -> p1.getAge() - p2.getAge()));
System.out.println(max2.orElse(null));
// 方式3:方法引用
Optional<Person> max3 = list.stream().collect(Collectors.maxBy(Comparator.comparingInt(Person::getAge)));
System.out.println(max3.orElse(null));
// 方式4:IDEA建议直接使用 max(),不要用 collect(Collector)
Optional<Person> max4 = list.stream().max(Comparator.comparingInt(Person::getAge));
System.out.println(max4.orElse(null));
// 特别是方式3和方式4,可以看做collect()聚合和max()聚合的对比
// 剩下的minBy和counting
Optional<Person> min1 = list.stream().collect(Collectors.minBy(Comparator.comparingInt(Person::getAge)));
Optional<Person> min2 = list.stream().min(Comparator.comparingInt(Person::getAge));
Long count1 = list.stream().collect(Collectors.counting());
Long count2 = list.stream().count();
}
@Data
@AllArgsConstructor
@NoArgsConstructor
static class Person {
private String name;
private Integer age;
private String address;
private Double salary;
}
}
分组
public class StreamTest {
private static List<Person> list;
static {
list = new ArrayList<>();
list.add(new Person("i", 18, "杭州", 999.9));
list.add(new Person("am", 19, "温州", 777.7));
list.add(new Person("iron", 21, "杭州", 888.8));
list.add(new Person("man", 17, "宁波", 888.8));
}
/**
* 按字段分组
* 按条件分组
*
* @param args
*/
public static void main(String[] args) {
// GROUP BY address
Map<String, List<Person>> groupingByAddress = list.stream().collect(Collectors.groupingBy(Person::getAddress));
System.out.println(groupingByAddress);
// GROUP BY address, age
Map<String, Map<Integer, List<Person>>> doubleGroupingBy = list.stream()
.collect(Collectors.groupingBy(Person::getAddress, Collectors.groupingBy(Person::getAge)));
System.out.println(doubleGroupingBy);
// 简单来说,就是collect(groupingBy(xx)) 扩展为 collect(groupingBy(xx, groupingBy(yy))),嵌套分组
// 解决了按字段分组、按多个字段分组,我们再考虑一个问题:有时我们分组的条件不是某个字段,而是某个字段是否满足xx条件
// 比如 年龄大于等于18的是成年人,小于18的是未成年人
Map<Boolean, List<Person>> adultsAndTeenagers = list.stream().collect(Collectors.partitioningBy(person -> person.getAge() >= 18));
System.out.println(adultsAndTeenagers);
}
@Data
@AllArgsConstructor
@NoArgsConstructor
static class Person {
private String name;
private Integer age;
private String address;
private Double salary;
}
}
统计
public class StreamTest {
private static List<Person> list;
static {
list = new ArrayList<>();
list.add(new Person("i", 18, "杭州", 999.9));
list.add(new Person("am", 19, "温州", 777.7));
list.add(new Person("iron", 21, "杭州", 888.8));
list.add(new Person("man", 17, "宁波", 888.8));
}
/**
* 统计
* @param args
*/
public static void main(String[] args) {
// 平均年龄
Double averageAge = list.stream().collect(Collectors.averagingInt(Person::getAge));
System.out.println(averageAge);
// 平均薪资
Double averageSalary = list.stream().collect(Collectors.averagingDouble(Person::getSalary));
System.out.println(averageSalary);
// 其他的不演示了,大家自己看api提示。简而言之,就是返回某个字段在某个纬度的统计结果
// 有个更绝的,针对某项数据,一次性返回多个纬度的统计结果:总和、平均数、最大值、最小值、总数,但一般用的很少
IntSummaryStatistics allSummaryData = list.stream().collect(Collectors.summarizingInt(Person::getAge));
long sum = allSummaryData.getSum();
double average = allSummaryData.getAverage();
int max = allSummaryData.getMax();
int min = allSummaryData.getMin();
long count = allSummaryData.getCount();
}
@Data
@AllArgsConstructor
@NoArgsConstructor
static class Person {
private String name;
private Integer age;
private String address;
private Double salary;
}
}
flatMap
总的来说,就是flatMap就是把多个流合并成一个流:
public class StreamTest {
private static List<Person> list;
static {
list = new ArrayList<>();
list.add(new Person("i", 18, "杭州", 999.9, new ArrayList<>(Arrays.asList("成年人", "学生", "男性"))));
list.add(new Person("am", 19, "温州", 777.7, new ArrayList<>(Arrays.asList("成年人", "打工人", "宇宙最帅"))));
list.add(new Person("iron", 21, "杭州", 888.8, new ArrayList<>(Arrays.asList("喜欢打篮球", "学生"))));
list.add(new Person("man", 17, "宁波", 888.8, new ArrayList<>(Arrays.asList("未成年人", "家里有矿"))));
}
public static void main(String[] args) {
Set<String> allTags = list.stream().flatMap(person -> person.getTags().stream()).collect(Collectors.toSet());
System.out.println(allTags);
}
@Data
@AllArgsConstructor
@NoArgsConstructor
static class Person {
private String name;
private Integer age;
private String address;
private Double salary;
// 个人标签
private List<String> tags;
}
}
forEach
简单来说就是遍历:
public class StreamTest {
private static List<Person> list;
static {
list = new ArrayList<>();
list.add(new Person("i", 18, "杭州", 999.9));
list.add(new Person("am", 19, "温州", 777.7));
list.add(new Person("iron", 21, "杭州", 888.8));
list.add(new Person("man", 17, "宁波", 888.8));
}
public static void main(String[] args) {
// 遍历操作,接收Consumer
list.stream().forEach(System.out::println);
// 简化,本质上不算同一个方法的简化
list.forEach(System.out::println);
}
@Data
@AllArgsConstructor
@NoArgsConstructor
static class Person {
private String name;
private Integer age;
private String address;
private Double salary;
}
}
匹配/查找
findFirst
public static void main(String[] args) {
Optional<Integer> first = Stream.of(1, 2, 3, 4)
.peek(v -> System.out.print(v + ","))
.findFirst();
}
结果:
1
只要第一个,后续元素不会继续遍历。
findAny
public static void main(String[] args) {
Optional<Integer> any = Stream.of(1, 2, 3, 4)
.peek(v -> System.out.print(v + ","))
.findAny();
}
结果:
1
findFirst和findAny几乎一样,但如果是并行流,结果可能不一致:
并行流颇有种“分而治之”的味道(底层forkjoin线程池),将流拆分并行处理,能较大限度利用计算资源,提高工作效率。但要注意,如果当前操作对顺序有要求,可能并不适合使用parallelStream。比如上图右边,使用并行流后返回的可能是4而不是1。