一 Stream API简介
java8新添加了一个特性:流Stream。Stream让开发者能够以一种声明的方式处理数据源(集合、数组等),它专注于对数据源进行各种高效的聚合操作(aggregate operation)和大批量数据操作 (bulk data operation)。
Stream API将处理的数据源看做一种Stream(流),Stream(流)在Pipeline(管道)中传输和运算,支持的运算包含筛选、排序、聚合等,当到达终点后便得到最终的处理结果。
注意: ①Stream 自己不会存储元素。 ②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。 ③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
二 Stream操作三个步骤
1. 创建 Stream(4种方式)由一个数据源(如:集合、数组)获取一个流
2. 中间操作(筛选与切片、映射、排序)一个中间操作链,对数据源的数据进行处理
3. 终止操作(也称为终端操作) 一个终止操作,执行中间操作链,并产生结果
三 创建Stream
@Test
public void test ( ) {
List < String > list = new ArrayList < > ( ) ;
Stream < String > stream = list. stream ( ) ;
Stream < String > parallelStream = list. parallelStream ( ) ;
Integer [ ] nums = new Integer [ 10 ] ;
Stream < Integer > stream1 = Arrays . stream ( nums) ;
Stream < Integer > stream2 = Stream . of ( 1 , 2 , 3 , 4 , 5 , 6 ) ;
Stream < Integer > stream3 = Stream . iterate ( 0 , ( x) -> x + 2 ) . limit ( 10 ) ;
stream3. forEach ( System . out:: println ) ;
Stream < Double > stream4 = Stream . generate ( Math :: random ) . limit ( 2 ) ;
stream4. forEach ( System . out:: println ) ;
}
四 中间操作
多个中间操作可以连接起来形成一个流水线,除非流水 线上触发终止操作,否则中间操作不会执行任何的处理! 而在终止操作时一次性全部处理,称为**“惰性求值”**。
4.1 筛选与切片
方法 描述 filter(Predicate p) 接收 Lambda,从流中排除某些元素 distinct() 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素 limit(long maxSize) 截断流,使其元素不超过给定数量 skip(long n) 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素 不足 n 个,则返回一个空流。与 limit(n) 互补
4.1.1 代码测试
import lombok. Data ;
@Data
public class Employee {
public Integer id;
public String name;
public Integer age;
public Integer price;
public Employee ( Integer id, String name, Integer age, Integer price) {
this . id = id;
this . name = name;
this . age = age;
this . price = price;
}
}
import org. junit. Test ;
import java. util. Arrays ;
import java. util. Iterator ;
import java. util. List ;
import java. util. stream. Stream ;
public class Oauth2DemoApplicationTests {
List < Employee > emps = Arrays . asList (
new Employee ( 101 , "王一" , 59 , 6666 ) ,
new Employee ( 102 , "李二" , 18 , 9999 ) ,
new Employee ( 103 , "张三" , 28 , 3333 ) ,
new Employee ( 105 , "王五" , 8 , 7777 ) ,
new Employee ( 105 , "王五" , 8 , 7777 ) ,
new Employee ( 106 , "赵六" , 8 , 77777 ) ,
new Employee ( 107 , "田七" , 38 , 5555 )
) ;
@Test
public void test1 ( ) {
Stream < Employee > employeeStream = emps. stream ( ) . filter ( ( e) -> {
return e. getAge ( ) >= 15 ;
}
) ;
employeeStream. forEach ( System . out :: println ) ;
}
@Test
public void test3 ( ) {
Iterator < Employee > it = emps. iterator ( ) ;
while ( it. hasNext ( ) ) {
System . out. println ( it. next ( ) ) ;
}
}
@Test
public void test4 ( ) {
emps. stream ( )
. limit ( 3 )
. forEach ( System . out:: println ) ;
emps. stream ( ) . filter ( e -> {
System . out. println ( "过滤数据" ) ;
return e. getPrice ( ) > 5000 ;
} ) . limit ( 2 ) . forEach ( System . out:: println ) ;
}
@Test
public void test5 ( ) {
emps. stream ( ) . skip ( 2 ) . forEach ( System . out:: println ) ;
}
@Test
public void test6 ( ) {
emps. stream ( ) . distinct ( ) . forEach ( System . out:: println ) ;
}
}
4.2 映射
方法 描述 map(Function f) 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素 mapToDouble(ToDoubleFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream mapToInt(ToIntFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream mapToLong(ToLongFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
import org. junit. Test ;
import java. util. ArrayList ;
import java. util. Arrays ;
import java. util. List ;
import java. util. stream. Stream ;
public class test2 {
List < Employee > emps = Arrays . asList (
new Employee ( 101 , "王一" , 59 , 6666 ) ,
new Employee ( 102 , "李二" , 18 , 9999 ) ,
new Employee ( 103 , "张三" , 28 , 3333 ) ,
new Employee ( 105 , "王五" , 8 , 7777 ) ,
new Employee ( 105 , "王五" , 8 , 7777 ) ,
new Employee ( 106 , "赵六" , 8 , 77777 ) ,
new Employee ( 107 , "田七" , 38 , 5555 )
) ;
@Test
public void test1 ( ) {
Stream < Object > str = emps. stream ( )
. map ( ( e) -> e. getName ( ) ) ;
str. forEach ( System . out:: println ) ;
System . out. println ( "-------------------------------------------" ) ;
List < String > strList = Arrays . asList ( "aaa" , "bbb" , "ccc" , "ddd" , "eee" ) ;
Stream < String > stream = strList. stream ( )
. map ( String :: toUpperCase ) ;
stream. forEach ( System . out:: println ) ;
Stream < Stream < Character > > stream2 = strList. stream ( )
. map ( test2:: filterCharacter ) ;
stream2. forEach ( ( e) -> {
e. forEach ( System . out:: println ) ;
} ) ;
System . out. println ( "---------------------------------------------" ) ;
Stream < Character > stream3 = strList. stream ( )
. flatMap ( test2:: filterCharacter ) ;
stream3. forEach ( System . out:: println ) ;
}
public static Stream < Character > filterCharacter ( String str) {
List < Character > list = new ArrayList < > ( ) ;
for ( Character ch : str. toCharArray ( ) ) {
list. add ( ch) ;
}
return list. stream ( ) ;
}
}
4.3 排序
方法 描述 sorted() 产生一个新流,其中按自然顺序排序 sorted(Comparator comp) 产生一个新流,其中按比较器顺序排序
import com. jin. oauth2demo. domain. Employee ;
import org. junit. Test ;
import java. util. Arrays ;
import java. util. List ;
public class test3 {
List < Employee > emps = Arrays . asList (
new Employee ( 101 , "王一" , 59 , 6666 ) ,
new Employee ( 102 , "李二" , 18 , 9999 ) ,
new Employee ( 103 , "张三" , 28 , 3333 ) ,
new Employee ( 105 , "王五" , 8 , 7777 ) ,
new Employee ( 105 , "王五" , 8 , 7777 ) ,
new Employee ( 106 , "赵六" , 8 , 77777 ) ,
new Employee ( 107 , "田七" , 38 , 5555 )
) ;
@Test
public void test2 ( ) {
emps. stream ( )
. map ( Employee :: getName )
. sorted ( )
. forEach ( System . out:: println ) ;
System . out. println ( "------------------------------------" ) ;
emps. stream ( )
. sorted ( ( x, y) -> {
if ( x. getAge ( ) == y. getAge ( ) ) {
return x. getName ( ) . compareTo ( y. getName ( ) ) ;
} else {
return Integer . compare ( x. getAge ( ) , y. getAge ( ) ) ;
}
} ) . forEach ( System . out:: println ) ;
}
}
对于定制排序,需要使用Lambda表达式取代Comparator函数式接口的匿名内部实现类重写compare方法。
五 终止操作
5.1 查找和匹配
方法 描述 allMatch(Predicate p) 检查是否匹配所有元素 anyMatch(Predicate p) 检查是否至少匹配一个元素 noneMatch(Predicate p) 检查是否没有匹配所有元素 findFirst() 返回第一个元素 findAny() 返回当前流中的任意元素 count() 返回流中元素总数 max(Comparator c) 返回流中最大值 min(Comparator c) 返回流中最小值 forEach(Consumer c) 内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代)
import org. junit. Test ;
import java. util. Arrays ;
import java. util. List ;
import java. util. Optional ;
public class test4 {
List < Employee > emps = Arrays . asList (
new Employee ( 101 , "王一" , 59 , 6666 ) ,
new Employee ( 102 , "李二" , 18 , 9999 ) ,
new Employee ( 103 , "张三" , 28 , 3333 ) ,
new Employee ( 105 , "王五" , 8 , 7777 ) ,
new Employee ( 105 , "王五" , 8 , 7777 ) ,
new Employee ( 106 , "赵六" , 8 , 77777 ) ,
new Employee ( 107 , "田七" , 38 , 5555 )
) ;
@Test
public void test1 ( ) {
boolean allMatch = emps. stream ( ) . allMatch ( e -> e. getAge ( ) == 8 ) ;
System . out. println ( "-----allMatch-----" + allMatch) ;
boolean anyMatch = emps. stream ( ) . anyMatch ( e -> e. getAge ( ) == 8 ) ;
System . out. println ( "-----anyMatch-----" + anyMatch) ;
boolean noneMatch = emps. stream ( ) . noneMatch ( e -> e. getAge ( ) == 8 ) ;
System . out. println ( "-----noneMatch-----" + noneMatch) ;
}
@Test
public void test2 ( ) {
Optional < Integer > first = emps. stream ( ) . map ( Employee :: getAge ) . sorted ( ) . findFirst ( ) ;
System . out. println ( "-----findFirst-----" + first) ;
Optional < Employee > any = emps. parallelStream ( ) . filter ( e -> e. getAge ( ) == 59 ) . sorted ( ) . findAny ( ) ;
System . out. println ( "-----findAny-----" + any) ;
long count = emps. stream ( ) . count ( ) ;
System . out. println ( "-----count-----" + count) ;
Optional < Integer > integerOptional = emps. stream ( )
. map ( Employee :: getAge )
. max ( Integer :: compare ) ;
System . out. println ( "-----max-----" + integerOptional. get ( ) ) ;
Optional < Employee > employeeOptional = emps. stream ( )
. min ( ( x, y) -> Integer . compare ( x. getAge ( ) , y. getPrice ( ) ) ) ;
System . out. println ( "-----min-----" + employeeOptional. get ( ) ) ;
}
}
5.2 归约
方法 描述 reduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。 返回 T reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。 返回 Optional< T>
@Test
public void test3 ( ) {
List < Integer > list = Arrays . asList ( 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 ) ;
Integer sum = list. stream ( )
. reduce ( 1 , ( x, y) -> x + y) ;
System . out. println ( sum) ;
}
5.3 收集
方法 描述 collect(Collector c) 将流转换为其他形式。接收一个 Collector接口的 实现,用于给Stream中元素做汇总的方法
收集案例
@Test
public void test3 ( ) {
List < String > list = emps. stream ( )
. map ( Employee :: getName )
. collect ( Collectors . toList ( ) ) ;
list. forEach ( System . out:: println ) ;
System . out. println ( "----------------------------------" ) ;
Set < String > set = emps. stream ( )
. map ( Employee :: getName )
. collect ( Collectors . toSet ( ) ) ;
set. forEach ( System . out:: println ) ;
System . out. println ( "----------------------------------" ) ;
HashSet < String > hs = emps. stream ( )
. map ( Employee :: getName )
. collect ( Collectors . toCollection ( HashSet :: new ) ) ;
hs. forEach ( System . out:: println ) ;
}
@Test
public void test4 ( ) {
Optional < Double > max = emps. stream ( )
. map ( Employee :: getSalary )
. collect ( Collectors . maxBy ( Double :: compare ) ) ;
System . out. println ( max. get ( ) ) ;
Optional < Employee > op = emps. stream ( )
. collect ( Collectors . minBy ( ( e1, e2) -> Double . compare ( e1. getSalary ( ) , e2. getSalary ( ) ) ) ) ;
System . out. println ( op. get ( ) ) ;
Double sum = emps. stream ( )
. collect ( Collectors . summingDouble ( Employee :: getSalary ) ) ;
System . out. println ( sum) ;
Double avg = emps. stream ( )
. collect ( Collectors . averagingDouble ( Employee :: getSalary ) ) ;
System . out. println ( avg) ;
获取员工数量
Long count = emps. stream ( )
. collect ( Collectors . counting ( ) ) ;
System . out. println ( count) ;
System . out. println ( "--------------------------------------------" ) ;
DoubleSummaryStatistics dss = emps. stream ( )
. collect ( Collectors . summarizingDouble ( Employee :: getSalary ) ) ;
System . out. println ( dss. getMax ( ) ) ;
System . out. println ( dss. getMin ( ) ) ;
System . out. println ( dss. getAverage ( ) ) ;
System . out. println ( dss. getSum ( ) ) ;
}
@Test
public void test5 ( ) {
Map < Status , List < Employee > > map = emps. stream ( )
. collect ( Collectors . groupingBy ( Employee :: getStatus ) ) ;
System . out. println ( map) ;
for ( Map. Entry entry: map. entrySet ( ) ) {
System . out. println ( entry. getKey ( ) + "--->" + entry. getValue ( ) ) ;
}
}
@Test
public void test6 ( ) {
Map < Status , Map < String , List < Employee > > > map = emps. stream ( )
. collect ( Collectors . groupingBy ( Employee :: getStatus , Collectors . groupingBy ( ( e) -> {
if ( e. getAge ( ) >= 60 )
return "老年" ;
else if ( e. getAge ( ) >= 35 )
return "中年" ;
else
return "成年" ;
} ) ) ) ;
System . out. println ( map) ;
for ( Map. Entry entry: map. entrySet ( ) ) {
Map < String , List < Employee > > map1 = ( Map < String , List < Employee > > ) entry. getValue ( ) ;
for ( Map. Entry entry1 : map1. entrySet ( ) ) {
System . out. println ( entry. getKey ( ) + "={" + entry1. getKey ( ) + "=" + entry1. getValue ( ) + "}" ) ;
}
}
}
@Test
public void test7 ( ) {
Map < Boolean , List < Employee > > map = emps. stream ( )
. collect ( Collectors . partitioningBy ( ( e) -> e. getSalary ( ) >= 5000 ) ) ;
System . out. println ( map) ;
}
@Test
public void test8 ( ) {
String str = emps. stream ( )
. map ( Employee :: getName )
. collect ( Collectors . joining ( "," , "----" , "----" ) ) ;
System . out. println ( str) ;
}
@Test
public void test9 ( ) {
Optional < Double > sum = emps. stream ( )
. map ( Employee :: getSalary )
. collect ( Collectors . reducing ( Double :: sum ) ) ;
System . out. println ( sum. get ( ) ) ;
}