Java 8 Stream流处理
- 1 什么是 Stream?
- 2 Stream的API
- 1 生成流
- 2 forEach
- 3 map
- 4 flatMap
- 5 filter
- 6 distinct
- 7 sorted
- 8 limit
- 9 reduce
- 10 collect
- 1 生成Collection
- 2 生成Map
- 3 字符串join
- 11 统计
1 什么是 Stream?
Stream(流) 是一个来自数据源的元素队列并支持聚合操作.
- 元素 是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
- 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
- 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
Stream操作还有两个基础的特征:
- Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
- 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor)实现。
2 Stream的API
常见API总结:
操作类型 | 接口方法 |
中间操作 | concat() distinct() filter() flatMap() limit() map() peek() skip() sorted() parallel() sequential() unordered() |
结束操作 | allMatch() anyMatch() collect() count() findAny() findFirst() forEach() forEachOrdered() max() min() noneMatch() reduce() toArray() |
区分中间操作和结束操作的方法,就是看方法的返回值,返回值为stream的是中间操作,否则是结束操作.
1 生成流
stream() − 为集合创建串行流
@Test
public void test(){
List<String> oldList = Arrays.asList("小红", "小蓝", "小粉", "小红", "小白", "小灰", "小青", "");
List<String> newList = oldList.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
System.out.println(newList);
}
//结果 过滤为空的元素
//[小红, 小蓝, 小粉, 小红, 小白, 小灰, 小青]
parallelStream() − 为集合创建并行流
并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流
引入: Fork/Join框架
不同的数据结构,使用parallel stream
的性能不同
性能 | 数据结构 |
Best | ArrayList [Concurrent]HashMaps plain arrays(阵列) |
Middle | … |
Worst | LinkedList BlockingQueues most IO-based sources |
@Test
public void test() {
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
// 获取空字符串的数量
long count = strings.parallelStream().filter(string -> string.isEmpty()).count();
System.out.println(count);
/* 结果
2
*/
}
2 forEach
Stream 提供了新的方法 forEach
来迭代流中的每个数据.
@Test
public void test(){
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
}
/* 结果
953463696
904482529
1992227049
228289483
1260884019
-694492700
-1895609817
390932419
222813973
1720446195
*/
3 map
map 方法用于映射每个元素到对应的结果.
@Test
public void test(){
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
// 获取对应的平方数
List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
System.out.println(squaresList);
//结果
//[9, 4, 49, 25]
}
4 flatMap
用于把原stream中的所有元素都"摊平"之后组成的Stream
,转换前后元素的个数和类型都可能会改变.
@Test
public void test() {
Stream<List<String>> stream = Stream.of(Arrays.asList("java", "python"), Arrays.asList("go","c", "c#"));
stream.flatMap(list -> list.stream())
.forEach(i -> System.out.println(i));
/* 结果
java
python
go
c
c#
*/
}
5 filter
filter 方法用于通过设置的条件过滤出元素.
@Test
public void test(){
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
// 获取空字符串的数量
long count = strings.stream().filter(string -> string.isEmpty()).count();
System.out.println(count);
//结果
//2
}
6 distinct
作用是返回一个去除重复元素.
@Test
public void test() {
Stream<String> stream= Stream.of("java", "python", "c++", "java", "python");
stream.distinct()
.forEach(str -> System.out.println(str));
/* 结果
java
python
c++
*/
}
7 sorted
sorted 方法用于对流进行排序.
- 自然顺序排序
Stream<T> sorted()
- 自定义比较器排序
Stream<T> sorted(Comparator<? super T> comparator)
@Test
public void test() {
Random random = new Random();
//自然顺序排序
random.ints().limit(10).sorted().forEach(System.out::println);
//自定义比较器排序
Stream<String> stream= Stream.of("I", "love", "you", "too");
stream.sorted((str1, str2) -> str1.length()-str2.length())
.forEach(str -> System.out.println(str));
/* 结果
-1333606821
-1309783057
-925151692
-151014012
326091384
631640690
931593578
1222302927
1666642944
1787379588
I
you
too
love
*/
}
8 limit
limit 方法用于获取指定数量的流.
@Test
public void test() {
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
/* 结果
590073840
1940673188
14366652
- 1137645939
1597476888
1548777482
198110723
1269388780
- 1953509555
- 1577770188
*/
}
9 reduce
reduce操作可以实现从一组元素中生成一个值,sum()
、max()
、min()
、count()
等.
Optional<T> reduce(BinaryOperator<T> accumulator)
T reduce(T identity, BinaryOperator<T> accumulator)
<U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)
@Test
public void test() {
//查询字符长度最长的字符
Stream<String> stream = Stream.of("java", "python", "c", "c++","c#");
Optional<String> longest = stream.reduce((s1, s2) -> s1.length()>=s2.length() ? s1 : s2);
//Optional<String> longest = stream.max((s1, s2) -> s1.length()-s2.length());
System.out.println(longest.get());
/* 结果
python
*/
}
@Test
public void test() {
// 求单词长度之和
Stream<String> stream = Stream.of("java", "python", "c", "c++","c#");
Integer lengthSum = stream.reduce(0, //初始值
(sum, str) -> sum+str.length(), //累加器
(a, b) -> a+b); //部分和拼接器,并行执行
// int lengthSum = stream.mapToInt(str -> str.length()).sum();
System.out.println(lengthSum);
/* 结果
16
*/
}
10 collect
用于将Stream转换成容器的方法.
1 生成Collection
Stream转换成List或Set
@Test
public void test() {
Stream<String> stream = Stream.of("java", "python", "c", "c++","c#");
Stream<String> stream1 = Stream.of("java", "python", "c", "c++","c#");
List<String> list = stream.collect(Collectors.toList());
Set<String> set = stream1.collect(Collectors.toSet());
System.out.println(list);
System.out.println(set);
/* 结果
[java, python, c, c++, c#]
[c#, python, c++, java, c]
*/
}
指定容器的实际类型Collectors.toCollection(Supplier<C> collectionFactory)
@Test
public void test() {
Stream<String> stream = Stream.of("java", "python", "c", "c++","c#");
Stream<String> stream1 = Stream.of("java", "python", "c", "c++","c#");
ArrayList<String> arrayList = stream.collect(Collectors.toCollection(ArrayList::new));
HashSet<String> hashSet = stream1.collect(Collectors.toCollection(HashSet::new));
System.out.println(arrayList);
System.out.println(hashSet);
/* 结果
[java, python, c, c++, c#]
[c#, python, c++, java, c]
*/
}
2 生成Map
-
Collectors.toMap()
指定如何生成Map的key和value -
Collectors.partitioningBy()
对元素进行二分区操作时用到 -
Collectors.groupingBy()
对元素做group操作时用到
Collectors.toMap
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String id;
private String name;
private Integer age;
private String sex;
}
@Test
public void test() {
//1 指定key-value,value是对象中的某个属性值
Map<Integer,String> userMap1 = userList.stream().collect(Collectors.toMap(User::getId,User::getName));
//2 指定key-value,value是对象本身
Map<Integer,User> userMap2 = userList.stream().collect(Collectors.toMap(User::getId,User->User));
//3 指定key-value,value是对象本身,Function.identity()是简洁写法,返回对象本身
Map<Integer,User> userMap3 = userList.stream().collect(Collectors.toMap(User::getId, Function.identity()));
//4 指定key-value,value是对象本身,Function.identity()是简洁写法,返回对象本身,key 冲突的解决办法,这里选择新的key覆盖旧的key
Map<Integer,User> userMap4 = userList.stream().collect(Collectors.toMap(User::getId, Function.identity(),(key1,key2)->key2));
}
Collectors.partitioningBy()
用于将Stream
中的元素依据某个二值逻辑(满足条件,或不满足)分成互补相交的两部分,如男女性别,及格与否
@Test
public void test() {
Map<Boolean, List<User>> userMap5 = userList.stream().collect(Collectors.partitioningBy(s -> s.getAge() >= 40));
}
Collectors.groupingBy()
按照某个属性对数据进行分组,属性相同的元素会被对应到Map的同一个key上.
@Test
public void test() {
//根据男女分组,分别统计两组的人数
Map<String, Integer> totalByDept = userList.stream()
.collect(Collectors.groupingBy(User::getSex(),
Collectors.counting()));// 下游收集器
}
//下游收集器中还可以包含更下游的收集器
@Test
public void test() {
//根据男女分组,并统计两组的所有人员名称
Map<String,List<String>> totalByDept = userList.stream()
.collect(Collectors.groupingBy(User::getSex(),
Collectors.mapping(User::getName,// 下游收集器
Collectors.toList())));// 更下游的收集器
}
3 字符串join
字符串拼接时使用Collectors.joining()
生成的收集器
@Test
public void test() {
// 使用Collectors.joining()拼接字符串
Stream<String> stream = Stream.of("I", "love", "you");
// String joined1 = stream.collect(Collectors.joining());
// System.out.println(joined1);
// String joined2 = stream.collect(Collectors.joining(","));
// System.out.println(joined2);
String joined3 = stream.collect(Collectors.joining(",", "{", "}"));
System.out.println(joined3);
/*结果
Iloveyou
I,love,you
{I,love,you}
*/
}
11 统计
一些产生统计结果的收集器也非常有用。主要用于int、double、long等基本类型上.
@Test
public void test() {
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
IntSummaryStatistics stats = numbers.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("列表中最大的数 : " + stats.getMax());
System.out.println("列表中最小的数 : " + stats.getMin());
System.out.println("所有数之和 : " + stats.getSum());
System.out.println("平均数 : " + stats.getAverage());
/* 结果
列表中最大的数 : 7
列表中最小的数 : 2
所有数之和 : 25
平均数 : 3.5714285714285716
*/
}