说明

  1. 流是 Java API 的新成员,允许程序员以声明式的方式处理集合数据,并且支持链式调用、支持并行处理。用流处理的集合数据高效且易读
  2. 流的API中可以分为两大类,中间操作终端操作,中间操作返回流对象,可以链式调用,终端操作则返回非流对象
  3. 流提供了很多方便的API,如筛选 filter、去重 distinct、截断 limit、跳过 skip、函数转换 map、扁平化 flatMap、判断流中是否有任意元素符合要求 anyMatch、是否所有元素都符合要求 allMatch、是否所有元素都不符合要求 noneMatch、查找元素 findAny findFirst、累计式的计算元素 reduce

参考

https://www.kancloud.cn/hanxt/javacrazy/1572238

推荐:https://mp.weixin.qq.com/s/Luv71KBF61LDH94Z1zAuCg

简单示例

1
2
3
4
5
6
7
8
9
10
11
12
List<String> nameStrs = Arrays.asList("Monkey", "Lion", "Giraffe","Lemur");

List<String> list = nameStrs.stream()
.filter(s -> s.startsWith("L"))
.map(String::toUpperCase)
.sorted()
.collect(toList());
System.out.println(list);

nameStrs.stream().map(s->{
return s+"_abc";
}).collect(Collectors.joining(",")); //Monkey_abc,Lion_abc,Giraffe_abc,Lemur_abc

转管道流

数组转管道流

1
2
3
4
String[] array = {"Monkey", "Lion", "Giraffe", "Lemur"};
Stream<String> nameStrs2 = Stream.of(array);

Stream<String> nameStrs3 = Stream.of("Monkey", "Lion", "Giraffe", "Lemur");

集合类对象转管道流

1
2
3
4
5
List<String> list = Arrays.asList("Monkey", "Lion", "Giraffe", "Lemur");
Stream<String> streamFromList = list.stream();

Set<String> set = new HashSet<>(list);
Stream<String> streamFromSet = set.stream();

文本文件转管道流

1
Stream<String> lines = Files.lines(Paths.get("file.txt"));

管道数据处理

过滤 - filter

1
list.stream().filter(Predicate<? super T> predicate)

映射 - map

map(String::toUpperCase)、map(String::length)、.mapToInt(String::length)、mapToLong、mapToDouble

  • peek函数是一种特殊的map函数,当函数没有返回值或者参数就是返回值的时候可以使用peek函数

    1
    2
    3
    4
    5
    List<Employee> maped = employees.stream()
    .peek(e -> {
    e.setAge(e.getAge() + 1);
    e.setGender(e.getGender().equals("M")?"male":"female");
    }).collect(Collectors.toList());
  • flatMap可以理解为将若干个子管道中的数据全都,平面展开到父管道中进行处理

    1
    2
    3
    4
    List<String> words = Arrays.asList("hello", "word");
    words.stream()
    .flatMap(w -> Arrays.stream(w.split(""))) // [h,e,l,l,o,w,o,r,l,d]
    .forEach(System.out::println);

List 排序 sorted

  • 默认的情况下,sorted 是按照字母的自然顺序进行排序

示例一

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
List<String> list = Arrays.asList("0002","0004","0001","0006","0003","0007","0009");
list = list.stream().sorted(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return Integer.parseInt(o1) > Integer.parseInt(o2)?1:-1; //升序
//return Integer.parseInt(o1) < Integer.parseInt(o2)?1:-1; //降序
}
}).collect(Collectors.toList());
for (String s : list) {
System.out.println(s);
}

List<Integer> records = new ArrayList<Integer>() {{
add(1);
add(2);
add(4);
}};
records = records.stream().sorted(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
//return o1.compareTo(o2); // 正序
return o2.compareTo(o1); // 倒序
}
}).collect(Collectors.toList());
System.out.println(records); // [4,2,1]


// 根据元素长度倒序排列
List<String> containsList = list.stream().sorted(Comparator.comparingInt(String::length).reversed()).collect(Collectors.toList());


// List<Event> records 根据任务时间倒序
records = records.stream().sorted((o1, o2) -> {
Date createTime_1 = o1.getTask().getCreateTime();
Date createTime_2 = o2.getTask().getCreateTime();
return DateUtil.compare(createTime_2, createTime_1);
}).collect(Collectors.toList());

// 分组
Map<String, List<YourObject>> groups = yourList.stream()
.collect(Collectors.groupingBy(YourObject::getGroupField));
// 返回指定字段值
Map<String, List<String>> result = groups.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().stream()
.map(YourObject::getTargetField)
.collect(Collectors.toList())
));

示例二

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// T 类的 age 属性类型为 Integer
List<T> list = list.stream().sorted(Comparator.comparing(T::getAge)).collect(Collectors.toList());

// T 类的 age 属性类型为 Integer,按照 age 倒序
List<T> list = list.stream().sorted(Comparator.comparing(T::getAge).reversed()).collect(Collectors.toList());

// T 类的 age 属性类型为String
List<T> list = list.stream().sorted(Comparator.comparing(T::getAge, Comparator.comparingInt(Integer::parseInt))).collect(Collectors.toList());

// T 类的 age 属性类型为String ,按照 age 倒序
List<T> list = list.stream().sorted(Comparator.comparing(T::getAge, Comparator.comparingInt(Integer::parseInt)).reversed()).collect(Collectors.toList());

// T 类的 sysCode 属性类型为String ,按照 sysCode 倒序
List<T> list = list.stream().sorted(Comparator.comparing(SysDepartArea::getSysCode, Comparator.comparingLong(Long::parseLong)).reversed()).collect(Collectors.toList());

根据多个字段排序

1
list = list.stream().sorted(Comparator.comparing(ProcessNodeVo::getType).thenComparing(ProcessNodeVo::getOrderNum)).collect(Collectors.toList());

根据指定字段排序,字段为空排后面 Comparator.nullsLast(Comparator.comparing

1
2
3
4
5
6
7
// 假设你已经有了一个NodeDocForm的列表
List<NodeDocForm> nodeDocForms = /* 初始化你的列表 */;

// 使用Stream API进行排序
List<NodeDocForm> sortedNodeDocForms = nodeDocForms.stream()
.sorted(Comparator.nullsLast(Comparator.comparing(NodeDocForm::getOrderNum)))
.collect(Collectors.toList());

Map 排序

按照 key 排序

1
2
3
4
5
6
7
8
9
// 按照 key 排序
Map<String, Long> orgCode2CountMap = orgCode2CountMap.entrySet().stream().sorted(Map.Entry.comparingByKey()).collect(
Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(oldVal, newVal) -> oldVal,
LinkedHashMap::new
)
);

按照 value 排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Map<String, Integer> map = new HashMap<>();
map.put("a",1);
map.put("d",4);
map.put("c",3);
map.put("e",5);
map.put("b",2);

// 排序后返回Map
Map<String, Integer> sortedMap2 = map.entrySet().stream()
// .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) // 按照 value 降序
.sorted(Map.Entry.comparingByValue()) // 按照 value 排序
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(oldVal, newVal) -> oldVal,
LinkedHashMap::new));

// 排序后返回Value的List
List<Integer> list = map.entrySet().stream()
//.sorted(Map.Entry.comparingByValue()) //升序
.map(Map.Entry::getValue)
.collect(Collectors.toList());

去重 - distinct

1
2
3
4
5
// distinct: list 中对象需实现 equals 和 hashCode
list.stream().distinct().collect(Collectors.toList());

// toSet 也可以实现去重
list.stream().collect(Collectors.toSet());

跳过前N个子元素 - skip

1
2
3
4
Stream.of("C", "B", "D", "F", "E", "A")
.sorted()// A,B,C,D,E,F
.skip(2) //C,D,E,F
.forEach(System.out::println);

返回最多N个元素 - limit

1
2
3
4
Stream.of(1,2,3,4,5,6,7,8)
.skip(2) //3,4,5,6,7,8
.limit(2) //3,4
.forEach(System.out::println);

anyMatch,allMatch,noneMatch

是否存在匹配 anyMatch,是否全部匹配 allMatch,是否全部不匹配 noneMatch

1
2
3
4
5
6
7
8
9
10
List<Integer> list = Arrays.asList(1, 2, 1, 1, 1);

// anyMatch:判断的条件里,任意一个元素成功,返回true
boolean anyMatch = list.stream().anyMatch(f -> f == (1)); // true

// allMatch:判断条件里的元素,所有的都是,返回true
boolean allMatch = list.stream().allMatch(f -> f == (1)); // false

// noneMatch:与allMatch相反,判断条件里的元素,所有的都不是,返回true
boolean noneMatch = list.stream().noneMatch(f -> f == (1)); // false

parallel 并行运算

表示对管道中的元素进行并行处理,而不是串行处理。但是这样就有可能导致管道流中元素的处理顺序无法保证。能够很好的利用CPU的多核处理器,达到更好的执行效率和性能,建议使用

  • parallel()更适合处理ArrayList,而不是LinkedList。ArrayList从数据结构上讲是基于数组的,可以根据索引很容易的拆分为多个
  • 适用于无状态操作:每个元素的计算都不得依赖或影响任何其他元素的计算,的运算场景
  • 基础数据源无变化:从文本文件里面边读边处理的场景,不适合parallel()并行处理。parallel()一开始就容量固定的集合,这样能够平均的拆分、同步处理

元素的收集

toList

1
List<String> list = Arrays.stream(arr).collect(Collectors.toList());

toSet

1
Set<String> set = Arrays.stream(arr).collect(Collectors.toSet());

toArray

1
String[] toArray = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion") .toArray(String[]::new);

toMap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
List<String> s = Arrays.asList("1", "2");
Map<String, String> collect = s.stream().collect(Collectors.toMap(String::toString, e -> e + "hello"));
System.out.println(collect); // {1=1hello, 2=2hello}

Map<String, String> userMap = allUser.stream().collect(Collectors.toMap(ComboModel::getUsername, ComboModel::getTitle));

Map<String, String> userDeptname = userList.stream().collect(Collectors.toMap(UserDto::getUserid, e->{
try {
return this.deptService.getDeptFullnameByDeptcode(e.getDeptcode());
} catch (ApplicationRuntimeException applicationRuntimeException) {
applicationRuntimeException.printStackTrace();
}
return "";
}));

//根据 ProcessInstanceId 分组取第一个
Map<String, Task> procInsIdTaskMap = taskList.stream().collect(Collectors.toMap(Task::getProcessInstanceId, a -> a, (k1, k2) -> k1));

Map<String, Integer> toMap = Stream.of(
"Monkey", "Lion", "Giraffe", "Lemur", "Lion"
).distinct()
.collect(Collectors.toMap(
Function.identity(), //元素输入就是输出,作为key
s -> (int) s.chars().distinct().count()// 输入元素的不同的字母个数,作为value
));
// 最终toMap的结果是: {Monkey=6, Lion=4, Lemur=5, Giraffe=6}

groupingBy

  • 根据指定字段分组 Collectors.groupingBy(Student::getAge)

  • 根据组合字段分组 Collectors.groupingBy(e->e.getName+"-"+e.getAge)

  • 先根据A字段分组,再根据B字段分组 Collectors.groupingBy(Student::getSubject)

  • 分组并计算每组元素个数 Collectors.counting()

  • 分组并计算每组指定字段总和 Collectors.summingLong(SysLog::getCostTime)、平均值Collectors.averagingLong(SysLog::getCostTime)

  • 分组并计算每组指定字段最大的对象 Collectors.maxBy(Comparator.comparing(Student::getScore))

  • 分组并将每组指定字段拼接起来

    Collectors.mapping(SysUsernameAppcode::getAppCode, Collectors.joining(","))

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
@Data
public class Student {
private String name;
private int age;
private int code;

public Student(String name, int age, int code) {
this.name = name;
this.age = age;
this.code = code;
}

public static void main(String[] args) {
Student student = new Student("小名",20,115);
Student student1 = new Student("小名",24,115);
Student student2 = new Student("小名",20,115);

List<Student> studentList = Arrays.asList(student, student1, student2);

//按照年龄分组
Map<Integer, List<Student>> ageMap = studentList.stream().collect(Collectors.groupingBy(Student::getAge));
System.out.println("ageMap = " + ageMap);

// 根据名称+年龄分组
Map<Integer, List<Student>> ageMap = studentList.stream().collect(Collectors.groupingBy(e->e.getName+"-"+e.getAge));

//按照年龄分组,并求出每组个数
Map<Integer, Long> ageMap2 = studentList.stream().collect(Collectors.groupingBy(Student::getAge, Collectors.counting()));
System.out.println("ageMap2 = " + ageMap2);

//按照年龄分组,并求出每组code平均值
Map<Integer, Double> age2 = studentList.stream().collect(Collectors.groupingBy(Student::getAge, Collectors.averagingInteger(SysLog::getCode)));

// 根据多个字段分组,并求出每组个数
Map<Integer, Long> ageMap3 = studentList.stream().collect(Collectors.groupingBy(e->e.getName+"-"+e.getAge, Collectors.counting()));

// 在分组的基础上获得分数最高的学生的信息
Map<String, Optional<Student>> map = students.stream().collect(Collectors.groupingBy(Student::getGrade,
Collectors.maxBy(Comparator.comparing(Student::getMark))));

// 先根据成绩分组,再根据名字分组
Map<Double,Map<String,List<Student>>> map2 =students.stream().collect(Collectors.groupingBy(Student::getMark,Collectors.groupingBy(Student::getSubject)));

// 先根据 grade 分组,再分为 合格和不合格 2组,并统计每组数量
Map<String,Map<String,Long>> map = students.stream().collect(Collectors.groupingBy(Student::getGrade,
Collectors.groupingBy(student -> {
if (student.getMark().intValue() >= 85){
return "合格";
}else{
return "不合格";
}
},Collectors.counting())
)
);
// {五年级={合格=3, 不合格=3}, 六年级={不合格=4, 合格=2}}
}
}

// 分组-组内排序,Comparator.comparingInt 升序
Map<String, List<EventDoc>> eventid2EventDocsMap = eventDocs.stream().sorted(Comparator.comparingInt(EventDoc::getOrderNum)).collect(Collectors.groupingBy(EventDoc::getEventId));

Map<Character, List<String>> groupingByList = Stream.of(
"Monkey", "Lion", "Giraffe", "Lemur", "Lion"
)
.collect(Collectors.groupingBy(
s -> s.charAt(0) , //根据元素首字母分组,相同的在一组
// counting() // 加上这一行代码可以实现分组统计
));
// 最终groupingByList内的元素: {G=[Giraffe], L=[Lion, Lemur, Lion], M=[Monkey]}
//如果加上counting() ,结果是: {G=1, L=3, M=1}


// 先分组,再根据某字段合并
Map<String, String> username2AppcodesMap = sysUsernameAppcodes.stream().collect(Collectors.groupingBy(SysUsernameAppcode::getUserName, Collectors.mapping(SysUsernameAppcode::getAppCode, Collectors.joining(","))));

统计

元素数量 - count

1
list.stream().xxxx().count()

最大值 max、最小值 min、平均值 average

求和 - sum

1
2
3
4
5
// 方式一
int score = examPaperQus.stream().mapToInt(ExamPaperQu::getScore).sum();

// 方式二
BigDecimal bb = list.stream().map(Plan::getAmount).reduce(BigDecimal.ZERO, BigDecimal::add);

全面统计

1
2
IntSummaryStatistics statistics = IntStream.of(1, 2, 3).summaryStatistics();
// 全面的统计结果statistics: IntSummaryStatistics{count=3, sum=6, min=1, average=2.000000, max=3}