参考资料
使用Stream.of()
方法,将数组转换为管道流。
使用Arrays.stream()
方法,将数组转换为管道流。
String[] array = {"Monkey", "Lion", "Giraffe", "Lemur"};
// 将数组转化为管道流
Stream<String> nameStrs2 = Stream.of(array);
Stream<String> nameStrs3 = Stream.of("Monkey", "Lion", "Giraffe", "Lemur");
String[] split = "hello".split("");
Stream<String> stream = Arrays.stream(split);
调用集合类的stream()
方法,将集合类对象转换为管道流。
// List转换为管道流
List<String> list = Arrays.asList("Monkey", "Lion", "Giraffe", "Lemur");
Stream<String> streamFromList = list.stream();
// Set转换为管道流
Set<String> set = new HashSet<>(list);
Stream<String> streamFromSet = set.stream();
// Map转换到管道流
Map<String, Integer> codes = new HashMap<>();
codes.put("United States", 1);
Stream<Map.Entry<String, Integer>> stream = codes.entrySet().stream();
通过Files.lines()
方法将文本文件转换为管道流
Stream<String> lines = Files.lines(Paths.get("文本文件所在的路径"));
准备一个实体类Employee
import java.util.function.Predicate;
public class Employee {
private Integer id;
private Integer age;
private String gender;
private String firstName;
private String lastName;
// filter函数中lambda表达式为一次性使用的谓词逻辑。
// 若我们的谓词逻辑需要被多处、多场景、多代码中使用,通常将它抽取出来单独定义到它所限定的主语实体中。
public static Predicate<Employee> ageGreaterThan70 = x -> x.getAge() > 70;
public static Predicate<Employee> genderM = x -> x.getGender().equals("M");
// 省略构造方法,get,set方法,toString()方法
}
List<Employee> employees = Arrays.asList("若干个Employee对象");
List<Employee> filtered = employees.stream()
// 由于使用了lambda表达式,此过滤逻辑只能在此处使用,如果想要在其他地方使用需要重新定义一遍
.filter(e -> e.getAge() > 70 && e.getGender().equals("M"))
.collect(Collectors.toList());
List<Person> filterList = persons.stream()
// 过滤出性别为1的person对象
.filter(p -> p.getSex().equals(1))
.collect(Collectors.toList());
List<Employee> filtered = employees.stream()
.filter(Employee.ageGreaterThan70.and(Employee.genderM))
.collect(Collectors.toList());
List<Employee> filtered = employees.stream()
.filter(Employee.ageGreaterThan70.or(Employee.genderM))
.collect(Collectors.toList());
List<Employee> filtered = employees.stream()
.filter(Employee.ageGreaterThan70.or(Employee.genderM).negate())
.collect(Collectors.toList());
map函数的作用就是针对管道流中的每一个数据元素进行转换操作。
List<String> alpha = Arrays.asList("Monkey", "Lion", "Giraffe", "Lemur");
// 使用Stream管道流
List<String> collect = alpha
.stream()
// 使用map将集合中的每一个元素都转换为大写
.map(String::toUpperCase)
.collect(Collectors.toList());
// 上面使用了方法引用,和下面的lambda表达式语法效果是一样的
List<String> collect = alpha
.stream().map(s -> s.toUpperCase())
.collect(Collectors.toList());
List<String> alpha = Arrays.asList("Monkey", "Lion", "Giraffe", "Lemur");
List<Integer> lengths = alpha.stream()
.map(String::length)
.collect(Collectors.toList());
System.out.println(lengths); // [6, 4, 7, 5]
Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
.mapToInt(String::length)
.forEach(System.out::println);
// 将0和1的List转换为Boolean的List
List<String> alpha = Arrays.asList("1", "0", "0", "1");
List<Boolean> collect = alpha.stream()
.map(ele -> "1".equals(ele))
.collect(Collectors.toList());
public static void main(String[] args){
// 准备一个List对象
Employee e1 = new Employee(1,23,"M","Rick","Beethovan");
Employee e2 = new Employee(2,13,"F","Martina","Hengis");
Employee e3 = new Employee(3,43,"M","Ricky","Martin");
Employee e4 = new Employee(4,26,"M","Jon","Lowman");
Employee e5 = new Employee(5,19,"F","Cristine","Maria");
Employee e6 = new Employee(6,15,"M","David","Feezor");
Employee e7 = new Employee(7,68,"F","Melissa","Roy");
Employee e8 = new Employee(8,79,"M","Alex","Gussin");
Employee e9 = new Employee(9,15,"F","Neetu","Singh");
Employee e10 = new Employee(10,45,"M","Naveen","Jain");
List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
// map的方式进行处理
List<Employee> maped = employees.stream()
.map(e -> {
// 将年龄全部+1
e.setAge(e.getAge() + 1);
// 对性别进行判断
e.setGender(e.getGender().equals("M") ? "male" : "female");
// 将处理后的对象返回
return e;
}).collect(Collectors.toList());
// peek的方式进行处理
List<Employee> maped = employees.stream()
// peek函数是一种特殊的map函数,当函数没有返回值或者参数就是返回值的时候可以使用peek函数
.peek(e -> {
e.setAge(e.getAge() + 1);
e.setGender(e.getGender().equals("M")?"male":"female");
}).collect(Collectors.toList());
System.out.println(maped);
}
Stream中间操作
无状态:
filter与map操作,不需要管道流的前面后面元素相关,所以不需要额外的记录元素之间的关系。输入一个元素,获得一个结果。就像班级点名就是无状态的,喊到你你就答到就可以了。
有状态:
sorted是排序操作、distinct是去重操作。像这种操作都是和别的元素相关的操作,我自己无法完成整体操作。如果是班级同学按大小个排序,那就不是你自己的事了,你得和周围的同学比一下身高并记住,你记住的这个身高比较结果就是一种“状态”。所以这种操作就是有状态操作。
List<String> limitN = Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
// limt方法传入一个整数n,用于截取管道中的前n个元素。
.limit(2)
.collect(Collectors.toList());
limitN.forEach(System.out::println); // [Monkey, Lion]
List<String> skipN = Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
// skip方法用于跳过前n个元素,截取从n到末尾的元素。
.skip(2)
.collect(Collectors.toList());
skipN.forEach(System.out::println); // [Giraffe, Lemur]
List<String> uniqueAnimals = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
.distinct()
.collect(Collectors.toList());
// 去重结果
["Monkey", "Lion", "Giraffe", "Lemur"]
默认的情况下,sorted是按照字母的自然顺序进行排序。
List<String> alphabeticOrder = Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
.sorted()
.collect(Collectors.toList());
// 去重结果
["Giraffe", "Lemur", "Lion", "Monkey"]
List<String> cities = Arrays.asList(
"Milan",
"london",
"San Francisco",
"Tokyo",
"New Delhi"
);
// 没有排序
System.out.println(cities); // [Milan, london, San Francisco, Tokyo, New Delhi]
// String.CASE_INSENSITIVE_ORDER(字母大小写不敏感)的规则排序
cities.sort(String.CASE_INSENSITIVE_ORDER);
System.out.println(cities); // [london, Milan, New Delhi, San Francisco, Tokyo]
// Comparator.naturalOrder()字母自然顺序排序
cities.sort(Comparator.naturalOrder());
System.out.println(cities); // [Milan, New Delhi, San Francisco, Tokyo, london]
// 使用Stream()的sorted的方式进行排序
cities.stream().sorted(Comparator.naturalOrder()).forEach(System.out::println);
//Milan
//New Delhi
//San Francisco
//Tokyo
//london
List<Integer> numbers = Arrays.asList(6, 2, 1, 4, 9);
System.out.println(numbers); // [6, 2, 1, 4, 9]
numbers.sort(Comparator.naturalOrder()); // 自然排序
System.out.println(numbers); // [1, 2, 4, 6, 9]
numbers.sort(Comparator.reverseOrder()); // 倒序排序
System.out.println(numbers); // [9, 6, 4, 2, 1]
List
排序准备数据
public class Employee {
private Integer id;
private Integer age;
private String gender;
private String firstName;
private String lastName;
// 省略get和set和toString()
}
Employee e1 = new Employee(1,23,"M","Rick","Beethovan");
Employee e2 = new Employee(2,13,"F","Martina","Hengis");
Employee e3 = new Employee(3,43,"M","Ricky","Martin");
Employee e4 = new Employee(4,26,"M","Jon","Lowman");
Employee e5 = new Employee(5,19,"F","Cristine","Maria");
Employee e6 = new Employee(6,15,"M","David","Feezor");
Employee e7 = new Employee(7,68,"F","Melissa","Roy");
Employee e8 = new Employee(8,79,"M","Alex","Gussin");
Employee e9 = new Employee(9,15,"F","Neetu","Singh");
Employee e10 = new Employee(10,45,"M","Naveen","Jain");
List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
升序
employees.sort(Comparator.comparing(Employee::getAge));
employees.forEach(System.out::println);
降序
employees.sort(Comparator.comparing(Employee::getAge).reversed());
先升序后降序
Comparator.reverseOrder()
employees.sort(
// 先按照性别的正序排序
Comparator.comparing(Employee::getGender)
// 然后再按照年龄的倒叙排序
.thenComparing(Employee::getAge, Comparator.reverseOrder())
);
employees.forEach(System.out::println);
// 打印结果
Employee{id=7, age=68, gender='F', firstName='Melissa', lastName='Roy'}
Employee{id=5, age=19, gender='F', firstName='Cristine', lastName='Maria'}
Employee{id=9, age=15, gender='F', firstName='Neetu', lastName='Singh'}
Employee{id=2, age=13, gender='F', firstName='Martina', lastName='Hengis'}
Employee{id=8, age=79, gender='M', firstName='Alex', lastName='Gussin'}
Employee{id=10, age=45, gender='M', firstName='Naveen', lastName='Jain'}
Employee{id=3, age=43, gender='M', firstName='Ricky', lastName='Martin'}
Employee{id=4, age=26, gender='M', firstName='Jon', lastName='Lowman'}
Employee{id=1, age=23, gender='M', firstName='Rick', lastName='Beethovan'}
Employee{id=6, age=15, gender='M', firstName='David', lastName='Feezor'}
sorted()
方法对其进行排序LinkedHashMap
(可以保留排序顺序)sorted()
方法以aComparator
作为参数,从而可以按任何类型的值对Map进行排序。
该函数应用场景就是当Key重复的时候,如何处理Map的元素值。
String k1 = "key";
String k2 = "key";
String k3 = "key3";
// 初始化一个HashMap,并向其中放入值
HashMap<String, Integer> map = new HashMap<String, Integer>() {{
put(k1, 1);
}};
// 由于k1和k2的值都是key,因此key发生冲突的时候,就启用参数三的lambda表达式
map.merge(k2, 2, (oldVal, newVal) -> oldVal + newVal);
map.merge(k3, 2, (oldVal, newVal) -> oldVal + newVal);
System.out.println(map);
// 打印结果
{
key3=2,
key=3
}
请注意使用LinkedHashMap
来存储排序的结果以保持顺序。
默认情况下,Collectors.toMap()
返回HashMap
。HashMap
不能保证元素的顺序。
// 创建一个Map,并放入数据
Map<String, Integer> codes = new HashMap<String, Integer>(){{
// 在初始化的时候,就放入数据
put("United States", 1);
put("Germany", 49);
put("France", 33);
put("China", 86);
put("Pakistan", 92);
}};
// 根据Map的键进行排序
Map<String, Integer> sortedMap = codes.entrySet().stream()
// 根据Map的key进行排序
.sorted(Map.Entry.comparingByKey())
.collect(
// 把Stream流排序后的结果收集为Map
Collectors.toMap(
// 使用Map原本的key和value
Map.Entry::getKey,
Map.Entry::getValue,
// 当主键有相同的情况,发生主键冲突的时候,使用旧主键所对应的值
(oldVal, newVal) -> oldVal,
// 收集之后的Map值一个LinkedHashMap类型(有顺序的Map)
LinkedHashMap::new
)
);
sortedMap.entrySet().forEach(System.out::println);
// 打印结果
China=86
France=33
Germany=49
Pakistan=92
United States=1
Map<String, Integer> sortedMap2 = codes.entrySet().stream()
// 根据Map的value进行排序
.sorted(Map.Entry.comparingByValue())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(oldVal, newVal) -> oldVal,
LinkedHashMap::new));
sortedMap2.entrySet().forEach(System.out::println);
// 打印结果
China=86
France=33
Germany=49
Pakistan=92
United States=1
TreeMap内的元素是有顺序的,所以利用TreeMap排序也是可取的一种方法。您需要做的就是创建一个TreeMap
对象,并将数据从HashMap
put到TreeMap
中
// 将HashMap转为TreeMap
Map<String, Integer> sorted = new TreeMap<>(codes);
sorted.entrySet().forEach(System.out::println);
// 打印结果
China=86
France=33
Germany=49
Pakistan=92
United States=1
在我们对数组或者集合类进行操作的时候,经常会遇到这样的需求,比如:
判断Stream流中是否包含某一个“匹配规则”的元素,返回一个布尔值.
// 判断employees列表中,是否有年龄 > 72岁的对象存在
boolean isExistAgeThan72 = employees.stream().anyMatch(e -> e.getAge() > 72);
判断是够Stream流中的所有元素都符合某一个"匹配规则",返回一个布尔值
// 判断employees列表中,是否所有的对象的年龄 > 10岁
boolean isExistAgeThan10 = employees.stream().allMatch(e -> e.getAge() > 10);
判断是否Stream流中的所有元素都不符合某一个"匹配规则",返回一个布尔值
// 判断employees列表中,是否有年龄 < 18岁的对象存在
boolean isExistAgeLess18 = employees.stream().noneMatch(e -> e.getAge() < 18);
查找第一个符合“匹配规则”的元素,返回值为Optional
// 返回第一个年龄 > 40岁的对象(可能有若干个这样的对象,但是只返回第一个)
Optional<Employee> employeeOptional
= employees.stream().filter(e -> e.getAge() > 40).findFirst();
System.out.println(employeeOptional.get());
查找任意一个符合“匹配规则”的元素,返回值为Optional
Optional<Employee> employeeOptional
= employees.stream().filter(e -> e.getAge() > 40).findAny();
System.out.println(employeeOptional.get());
Stream API为我们提供了Stream.reduce
用来实现集合元素的归约。reduce函数有三个参数:
// 1 + 2 + 3 + 4 + 5 + 6
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
int result = numbers
.stream()
// 参数1为0,代码累加的初始值
// 参数2为一个函数,表示元素累加
.reduce(0, (subtotal, element) -> subtotal + element);
System.out.println(result); // 21
int result = numbers
.stream()
// 进行归约
.reduce(0, Integer::sum);
System.out.println(result); // 21
不仅可以归约Integer类型,只要累加器参数类型能够匹配,可以对任何类型的集合进行归约计算。
List<String> letters = Arrays.asList("a", "b", "c", "d", "e");
String result = letters
.stream()
// 初始值是一个空字符串
.reduce("", (partialString, element) -> partialString + element);
System.out.println(result); // abcde
String result = letters
.stream()
.reduce("", String::concat);
System.out.println(result); // abcde
准备数据
Employee e1 = new Employee(1,23,"M","Rick","Beethovan");
Employee e2 = new Employee(2,13,"F","Martina","Hengis");
Employee e3 = new Employee(3,43,"M","Ricky","Martin");
Employee e4 = new Employee(4,26,"M","Jon","Lowman");
Employee e5 = new Employee(5,19,"F","Cristine","Maria");
Employee e6 = new Employee(6,15,"M","David","Feezor");
Employee e7 = new Employee(7,68,"F","Melissa","Roy");
Employee e8 = new Employee(8,79,"M","Alex","Gussin");
Employee e9 = new Employee(9,15,"F","Neetu","Singh");
Employee e10 = new Employee(10,45,"M","Naveen","Jain");
List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);
计算列表中所有对象的年龄的和
Integer total = employees.stream()
// 先获取出所有对象的年龄
.map(Employee::getAge)
// 对所有的年龄进行归约,求和
.reduce(0,Integer::sum);
System.out.println(total); // 346
Integer total3 = employees.stream()
// 没有使用map来获取对象中的年龄,所以Stream流中的数据实际上是employe对象
// 注意这里reduce方法有三个参数
.reduce(0,(totalAge,emp) -> totalAge + emp.getAge(),Integer::sum);
System.out.println(total); // 346
并行流数据归约(使用合并器)
对于大数据量的集合元素归约计算,更能体现出Stream并行流计算的威力。
在进行并行流计算的时候,可能会将集合元素分成多个组计算。为了更快的将分组计算结果累加,可以使用合并器。
Integer total2 = employees
.parallelStream()
.map(Employee::getAge)
// 注意这里reduce方法有三个参数
.reduce(0,Integer::sum,Integer::sum);
System.out.println(total); //346
⏹获取最长的字符串
List<String> list = Arrays.asList("abc", "egjsdfhkj", "sdf", "xbasefwefefngd", "eeeeeeeeeeeeeee");
String str = list.stream().max(Comparator.comparing(String::length)).orElse("");
System.out.println(str); // eeeeeeeeeeeeeee
⏹获取最大的数值
// Java9的数组创建方式
List<Integer> integers = List.of(7, 6, 9, 4, 11, 6);
integers.stream().max(Integer::compareTo).ifPresent(System.out::println); // 11
⏹获取工资最高的人
// 获取工资最高的人
List<Person> personList = new ArrayList<>();
personList.add(new Person("Tom", 8900, 23, "male", "New York"));
personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
personList.add(new Person("Anni", 8200, 24, "female", "New York"));
personList.add(new Person("Owen", 9500, 25, "male", "New York"));
personList.add(new Person("Alisa", 7900, 26, "female", "New York"));
Optional<Person> maxPerson = personList.stream().max(Comparator.comparingInt(Person::getSalary));
maxPerson.ifPresent(System.out::println);
// Person{name='Owen', salary=9500, age=25, sex='male', area='New York'}
参考这篇博客: https://blog.csdn.net/feyehong/article/details/125640378
java管道流
若只是希望将Stream管道流的处理结果打印出来,而不是进行类型转换,
我们就可以使用forEach()方法或forEachOrdered()方法。
Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
.parallel()
.forEach(System.out::println);
Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
.parallel()
.forEachOrdered(System.out::println);
parallel()
函数表示对管道中的元素进行并行处理,而不是串行处理,这样处理速度更快。但是这样就有可能导致管道流中后面的元素先处理,前面的元素后处理,也就是元素的顺序无法保证forEachOrdered
从名字上看就可以理解,虽然在数据处理顺序上可能无法保障,但是forEachOrdered方法可以在元素输出的顺序上保证与元素进入管道流的顺序一致。java Stream 最常见的用法就是:
将集合类转换成管道流。
对管道流数据处理。
将管道流处理结果在转换成集合类。
collect()方法就为我们提供了这样的功能:将管道流处理结果在转换成集合类。
Set<String> collectToSet = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
.collect(Collectors.toSet());
// 最终collectToSet 中的元素是:[Monkey, Lion, Giraffe, Lemur],注意Set会去重。
List<String> collectToList = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
.collect(Collectors.toList());
// 最终collectToList中的元素是: [Monkey, Lion, Giraffe, Lemur, Lion]
2022/07/06 补充
Java17 对收集为List做了进一步简化,不需要使用 .collect()进行收集,直接使用.toList()
进行转换即可
List<String> collectToList = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion").toList();
// 最终collectToList中的元素是: [Monkey, Lion, Giraffe, Lemur, Lion]
通过toArray(String[]::new)方法收集Stream的处理结果,将所有元素收集到字符串数组中。
String[] toArray = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
.toArray(String[]::new);
// 最终toArray字符串数组中的元素是: [Monkey, Lion, Giraffe, Lemur, Lion]
使用Collectors.toMap()方法将数据元素收集到Map里面,但是出现一个问题:那就是管道中的元素是作为key,还是作为value。
我们用到了一个Function.identity()方法,该方法很简单就是返回一个“ t -> t ”(输入就是输出的lambda表达式)。另外使用管道流处理函数distinct()
来确保Map键值的唯一性。
Map<String, Integer> toMap =
Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
.distinct()
.collect(Collectors.toMap(
// 元素输入就是输出,作为key
Function.identity(),
// 输入元素的不同的字母个数,作为value
s -> (int) s.chars().distinct().count()
)
);
// 最终toMap的结果是: {Monkey=6, Lion=4, Lemur=5, Giraffe=6}
⏹收集为Map方式
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom", 8900, 23, "male", "New York"));
personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
personList.add(new Person("Anni", 8200, 24, "female", "New York"));
// 1.⏹对象List收集为Map
Map<String, String> collect = personList.stream()
.collect(
Collectors.toMap(
// name作为key
Person::getName,
// sex作为value
Person::getSex
)
);
System.out.println(collect);
// 2.⏹对象List收集为Map
Map<String, Person> personMap = personList.stream()
.collect(
Collectors.toMap(
// person中的name作为key
Person::getName,
//person实体类原封不动的作为value
item -> item
)
);
System.out.println(personMap);
// {Tom=Person{name='Tom', salary=8900, age=23, sex='male', area='New York'}, Anni=Person{name='Anni', salary=8200, age=24, sex='female', area='New York'}, ...省略...}
Map<Integer, String> genderValNameMap = new HashMap<>() {
{
put(1, "男");
put(0, "女");
}
};
System.out.println(genderValNameMap); // {0=女, 1=男}
Map<String, Integer> genderNameValMap = genderValNameMap.entrySet().stream().collect(
Collectors.toMap(
Map.Entry::getValue,
Map.Entry::getKey
)
);
System.out.println(genderNameValMap); // {女=0, 男=1}
将流中的元素,用指定的连接符拼接起来
List<String> list = Arrays.asList("A", "B", "C");
String string = list.stream().collect(Collectors.joining("-"));
System.out.println("拼接后的字符串:" + string); // 拼接后的字符串:A-B-C
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom", 8900, 23, "male", "New York"));
personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
String names = personList.stream()
.map(p -> p.getName()).collect(Collectors.joining(","));
System.out.println("所有员工的姓名:" + names); // Tom,Jack,Lily
准备数据
import java.math.BigDecimal;
public class Product {
// 商品id
private Long id;
// 商品数量
private Integer num;
// 商品价格
private BigDecimal price;
// 商品名称
private String name;
// 商品种类
private String category;
public Product(Long id, Integer num, BigDecimal price,
String name, String category) {
this.id = id;
this.num = num;
this.price = price;
this.name = name;
this.category = category;
}
// 省略get和set和toString()
}
// 创建若干个对象,然后放到List中
Product prod1 = new Product(1L, 1, new BigDecimal("15.5"), "面包", "零食");
Product prod2 = new Product(2L, 2, new BigDecimal("20"), "饼干", "零食");
Product prod3 = new Product(3L, 3, new BigDecimal("30"), "月饼", "零食");
Product prod4 = new Product(4L, 3, new BigDecimal("10"), "青岛啤酒", "啤酒");
Product prod5 = new Product(5L, 10, new BigDecimal("15"), "百威啤酒", "啤酒");
List<Product> prodList = Arrays.asList(prod1, prod2, prod3, prod4, prod5);
// 按照category进行分类
Map<String, List<Product>> categoryMap = prodList.stream().collect(Collectors.groupingBy(Product::getCategory));
System.out.println(categoryMap);
// 打印结果
{
啤酒=[Product{id=4, num=3, price=10, name='青岛啤酒', category='啤酒'},
Product{id=5, num=10, price=15, name='百威啤酒', category='啤酒'}],
零食=[Product{id=1, num=1, price=15.5, name='面包', category='零食'},
Product{id=2, num=2, price=20, name='饼干', category='零食'},
Product{id=3, num=3, price=30, name='月饼', category='零食'}]
}
// 按照属性拼接分组
Map<String, List<Product>> categoryMap1 = prodList.stream().collect(Collectors.groupingBy(item -> {
return item.getCategory() + "_" + item.getName();
}));
System.out.println(categoryMap1);
// 打印结果
{
零食_月饼=[Product{id=3, num=3, price=30, name='月饼', category='零食'}],
零食_面包=[Product{id=1, num=1, price=15.5, name='面包', category='零食'}],
啤酒_百威啤酒=[Product{id=5, num=10, price=15, name='百威啤酒', category='啤酒'}],
啤酒_青岛啤酒=[Product{id=4, num=3, price=10, name='青岛啤酒', category='啤酒'}],
零食_饼干=[Product{id=2, num=2, price=20, name='饼干', category='零食'}]
}
// 商品数量是否小于3进行分组
Map<String, List<Product>> categoryMap2 = prodList.stream().collect(Collectors.groupingBy(item -> {
return item.getNum() < 3 ? "小于3" : "other";
}));
System.out.println(categoryMap2);
// 打印结果
{
other=[
Product{id=3, num=3, price=30, name='月饼', category='零食'},
Product{id=4, num=3, price=10, name='青岛啤酒', category='啤酒'},
Product{id=5, num=10, price=15, name='百威啤酒', category='啤酒'}],
小于3=[
Product{id=1, num=1, price=15.5, name='面包', category='零食'},
Product{id=2, num=2, price=20, name='饼干', category='零食'}
]
}
要实现多级分组,我们可以使用一个由双参数版本
的Collectors.groupingBy工厂方法创 建的收集器,它除了普通的分类函数之外,还可以接受collector类型的第二个参数。
要进 行二级分组的话,我们可以把一个内层groupingBy传递给外层groupingBy,并定义一个为流 中项目分类的二级标准。
Map<String, Map<String, List<Product>>> categoryMap3 =
prodList.stream().collect(
// 分组
Collectors.groupingBy(
// 先根据产品类型分组
Product::getCategory,
// 然后再根据产品数量是否小于3进行分组
Collectors.groupingBy(item -> {
return item.getNum() < 3 ? "小于3" : "other";
})
)
);
System.out.println(categoryMap3);
// 打印结果
{
啤酒={
other=[
Product{id=4, num=3, price=10, name='青岛啤酒', category='啤酒'},
Product{id=5, num=10, price=15, name='百威啤酒', category='啤酒'}
]
},
零食={
other=[
Product{id=3, num=3, price=30, name='月饼', category='零食'}
],
小于3=[
Product{id=1, num=1, price=15.5, name='面包', category='零食'},
Product{id=2, num=2, price=20, name='饼干', category='零食'}
]
}
}
// 求种类的总数
Map<String, Long> mapCount = prodList.stream().
collect(
Collectors.groupingBy(
// 根据种类型进行分组
Product::getCategory,
// 计算每个种类的数量
Collectors.counting()
)
);
System.out.println(mapCount);
// 打印结果
{
// 一共有两种啤酒
啤酒=2,
// 一共有三种零食
零食=3
}
// 根据种类求总的数量
Map<String, Integer> mapAllCount = prodList.stream()
.collect(
Collectors.groupingBy(
// 根据类型进行分组
Product::getCategory,
// 计算每个类型的总的数量
Collectors.summingInt(Product::getNum)
)
);
System.out.println(mapAllCount);
// 打印结果
{
// 啤酒一共有13个(10个百威啤酒,3个青岛啤酒)
啤酒=13,
// 零食一共有6个(1个面包,2个饼干,3个月饼)
零食=6
}
// 每种商品中数量最多的
Map<String, Product> collect = prodList.stream()
.collect(
Collectors.toMap(
// 根据商品种类进行分组
Product::getCategory,
// 元素输入就是输出
Function.identity(),
// 根据商品的数量进行比较,取数量最多的商品
BinaryOperator.maxBy(Comparator.comparingInt(Product::getNum)
)
)
);
System.out.println(collect);
// 打印结果
{
// 啤酒类商品中,百威啤酒最多,有10瓶
啤酒=Product{id=5, num=10, price=15, name='百威啤酒', category='啤酒'},
// 零食类商品中,月饼最多,有3个
零食=Product{id=3, num=3, price=30, name='月饼', category='零食'}
}
Map<String, Set<String>> prodMap = prodList.stream()
.collect(
Collectors.groupingBy(
// 根据商品的种类进行分组
Product::getCategory,
// 对商品的名称进行遍历,使用Set进行去重
Collectors.mapping(Product::getName, Collectors.toSet())
)
);
System.out.println(prodMap);
// 打印结果
{
啤酒=[青岛啤酒, 百威啤酒],
零食=[面包, 饼干, 月饼]
}
需求:根据国家地区code进行分组,将消息code和消息放入Map中
⏹实体类
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class I18MessageEnttiy {
// 消息code
private String code;
// 国家地区码
private String locale;
// 消息
private String item;
}
List<I18MessageEnttiy> allLocaleMessage = Arrays.asList(
new I18MessageEnttiy("M001", "zh", "姓名"),
new I18MessageEnttiy("M001", "ja", "名前"),
new I18MessageEnttiy("M002", "zh", "邮箱"),
new I18MessageEnttiy("M002", "ja", "メールアドレス"),
new I18MessageEnttiy("M003", "zh", "电视"),
new I18MessageEnttiy("M003", "ja", "テレビ"),
new I18MessageEnttiy("M004", "zh", "中文系统"),
new I18MessageEnttiy("M004", "ja", "日本語システム")
);
// 将查询到的国际化资源转换为 Map<地区码, Map> 的数据格式
Map<String, Map<String, String>> localeMsgMap = allLocaleMessage
// stream流
.stream()
// 分组
.collect(Collectors.groupingBy(
// 根据国家地区分组
I18MessageEnttiy::getLocale,
// 收集为Map,key为code,value为信息
Collectors.toMap(
I18MessageEnttiy::getCode
, I18MessageEnttiy::getItem
)
));
System.out.println(localeMsgMap);
/*
{
ja={
M004=日本語システム
, M003=テレビ
, M002=メールアドレス
, M001=名前
}
, zh={
M004=中文系统
, M003=电视
, M002=邮箱
, M001=姓名
}
}
*/
实体类List
List<Person> personList = new ArrayList<>();
personList.add(new Person("Tom", 8900, 23, "male", "New York"));
personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
personList.add(new Person("Anni", 8200, 24, "female", "New York"));
⏹将员工按薪资是否高于8000分组
Map<Boolean, List<Person>> part = personList.stream()
.collect(
Collectors.partitioningBy(item -> item.getSalary() > 8000)
);
System.out.println(part);
/*
{
false=[
Person{name='Jack', salary=7000, age=25, sex='male', area='Washington'},
Person{name='Lily', salary=7800, age=21, sex='female', area='Washington'}
],
true=[
Person{name='Tom', salary=8900, age=23, sex='male', area='New York'},
Person{name='Anni', salary=8200, age=24, sex='female', area='New York'}
]
}
*/
⏹将员工按性别分组
Map<String, List<Person>> group = personList.stream()
.collect(
Collectors.groupingBy(Person::getSex)
);
System.out.println(group);
/*
{
female=[
Person{name='Lily', salary=7800, age=21, sex='female', area='Washington'},
Person{name='Anni', salary=8200, age=24, sex='female', area='New York'}
],
male=[
Person{name='Tom', salary=8900, age=23, sex='male', area='New York'},
Person{name='Jack', salary=7000, age=25, sex='male', area='Washington'}
]
}
*/
⏹将员工先按性别分组,再按地区分组
Map<String, Map<String, List<Person>>> group2 = personList.stream()
.collect(
// 进行分组收集
Collectors.groupingBy(
// 先按照性别分组
Person::getSex,
// 然后再按照区域收集
Collectors.groupingBy(Person::getArea)
)
);
System.out.println(group2);
/*
{
female={
New York=[
Person{name='Anni', salary=8200, age=24, sex='female', area='New York'}
],
Washington=[
Person{name='Lily', salary=7800, age=21, sex='female', area='Washington'}
]
},
male={
New York=[
Person{name='Tom', salary=8900, age=23, sex='male', area='New York'}
],
Washington=[
Person{name='Jack', salary=7000, age=25, sex='male', area='Washington'}
]
}
}
*/
可以将数据元素收集到任意的Collection类型:即向所需Collection类型提供构造函数的方式。
LinkedList<String> collectToCollection = Stream.of(
"Monkey", "Lion", "Giraffe", "Lemur", "Lion"
).collect(Collectors.toCollection(LinkedList::new));
// 最终collectToCollection中的元素是: [Monkey, Lion, Giraffe, Lemur, Lion]