1、Stream简介
Java 8中的集合支持一个新的stream方法,它会返回一个流(接口定义在java.util.stream.Stream里),从支持数据处理操作的源生成的元素序列
- 元素序列:像集合一样,流也提供了一个接口,可以访问特定元素类型的一组有序值,集合讲的是数据,流讲的是计算
- 源:流会使用一个提供数据的源,如集合、数组或输入/输出资源
- 数据处理操作:类似于数据库的操作,以及函数式编程语言中的常用操作,如filter、map、reduce、find、match、sort等。流操作可以顺序执行,也可并行执行。
流操作有两个重要的特点:
- 内部迭代:流遵循“做什么而非怎么做”,流的迭代操作是在背后进行的。
- 流水线:很多流操作本身会返回一个流,这样多个操作就可以链接起来,形成一个大的流水线。
Stream表面看起来与集合很类似,都可以让我们转换和获取数据但是它们之间存在着显著的差异:
- Stream不存储数据。(元素可能存储在底层的集合中,或者按需生成)
- Stream不修改数据源。(例如:filter方法不会从流中移除元素,而是会生成一个新的流,其中不包含被过滤掉的元素)
- Stream尽可能的惰性执行。(或称之为延迟执行特性,这意味着直至需要其结果时,操作才会执行)
2、使用Stream流
流的使用一般包括三件事
- 创建Stream流:(一个数据源(如集合)来执行一个查询)
- 中间操作:(指定将初始流转换为其他流,可能包含多个步骤,形成一条流的流水线)
- 终端操作:(执行流水线,并能生成结果)
2.1、创建Stream流
1、Stream方法创建流
of
static < T > Stream < T > of (T t) 产生一个元素为给定值的流
@Test
public void test1(){
Stream<String> stream = Stream.of("Hello", "Stream", "Bye");
stream.forEach(System.out::println);
}
empty
static < T > Stream < T > empty( ) 产生一个不包含任何元素的流
@Test
public void test2(){
Stream<Object> stream = Stream.empty();
stream.forEach(System.out::println);
}
iterate
static < T > Stream < T > iterate( final T seed, final UnaryOperator < T > f )产生一个无限流,参数seed调用函数f规则产生的值下创建一个无限流
@Test
public void test3(){
Stream<Integer> stream = Stream.iterate(1, x -> x + 2).limit(5); // 1 3 5 7 9
stream.forEach(System.out::println);
}
generate
static< T > Stream< T > generate( Supplier< T > s ) 产生一个无限流,通过反复调用函数s而构建
@Test
public void test4(){
Stream<Integer> stream = Stream.generate(()->(int)(Math.random()*100)).limit(5);
stream.forEach(System.out::println);
}
2、数组创建流
static < T > Stream< T > stream( T[] array)产生一个流,数组中元素构成
static < T > Stream< T > stream( T[] array, int startInclusive, int endExclusive )产生一个流,由数组中指定范围的元素构成
@Test
public void test5(){
Integer[] integers = {1, 3, 5, 7, 9};
Arrays.stream(integers,1,4).forEach(System.out::println); // 3 5 7
Arrays.stream(integers).forEach(System.out::println);// 1 3 5 7 9
}
3、集合创建流
Java8 中的 Collection 接口被扩展, 供了两个获取流的方法
default Stream< E> stream()返回一个顺序流
default Stream< E> parallelStream() : 返回一个并行流(并行流是多线程方式,需要考虑线程安全问题)
@Test
public void test6(){
List<Integer> list = Arrays.asList(2, 4, 6, 8, 10);
list.stream().limit(3).forEach(System.out::println);
List<String> stringList = Arrays.asList("Hello", "Stream", "parallelStream");
stringList.parallelStream().forEach(System.out::println);
}
4、文件创建流
static Stream< String > lines(Path path) 产生一个流,指定文件行元素构成
@Test
public void test7() throws IOException {
Stream<String> stream = Files.lines(Paths.get("D:\\fileStream.txt")).limit(2);
stream.forEach(System.out::println);
}
2.2、中间操作
在将初始流转换为其他流,可能包含多个操作,将多个操作可以连接起来形成一个流水线查询,每一个中间操作就像流水线上的一个工人,每人工人都可以对流进行加工,加工后得到的结果还是一个流,除非流水线上触发终止操作,否则中间操作不会执行任何的处理! 而在终止操作时一次性全部处理,称为“惰性求值”。
1、filter方法
Stream< T > filter( Predicate<? super T> predicate ); 产生一个流,包含当前流中所有满足predicate条件的元素
@Test
public void test1(){
List<String> list = Arrays.asList("hello", "stream", "filter", "best");
list.stream().filter(x->x.contains("l")).forEach(System.out::println); // hello filter
}
2、map方法
< R > Stream < R > map(Function<? super T, ? extends R> mapper); 产生一个流,由mapper应用到当前流中所有元素产生的结果构成
@Test
public void test2(){
List<String> list = Arrays.asList("hello", "stream", "map");
list.stream().map(String::toUpperCase).forEach(System.out::println); //HELLO STREAM MAP
}
3、flatMap方法
< R > Stream< R> flatMap( Function<? super T, ? extends Stream<? extends R>> mapper );产生一个流,将mapper应用与当前流中所有元素所 产生的流 连接一起构成
@Test
public void test3(){
List<Integer> list = Arrays.asList(1, 2, 3);
list.stream().flatMap(x -> Stream.of(x*10).filter(y->y>15))
.forEach(System.out::println);//20 30
}
4、limit方法
Stream< T > limit(long maxSize); 产生一个流,由当前流最初的maxSize个元素构成
@Test
public void test4(){
List<String> list = Arrays.asList("hello", "stream", "limit","best");
list.stream().limit(2)
.forEach(System.out::println);// hello stream
}
5、skip方法
Stream< T > skip(long n); 产生一个流,由当前流除前n个元素之外的所有元素构成
@Test
public void test5(){
List<String> list = Arrays.asList("hello", "stream", "skip","best");
list.stream().skip(2)
.forEach(System.out::println); // skip best
}
6、distinct方法
Stream< T > distinct(); 产生一个流,由当前流去重后构成
@Test
public void test6(){
List<String> list = Arrays.asList("hello", "stream", "hello","distinct");
list.stream().distinct()
.forEach(System.out::println);// hello stream distinct
}
7、sorted方法
Stream< T > sorted(Comparator<? super T> comparator); 产生一个流,由当前流按照comparator顺序排列产生
@Test
public void test7(){
List<Integer> list = Arrays.asList(12, 3, 45,7);
list.stream().sorted((x,y)->y.compareTo(x))
.forEach(System.out::println); // 45 12 7 3
}
8、peek方法
Stream< T > peek(Consumer<? super T> action); 产生一个流,每次获取元素会将其传递给action,调试比较方便,便于验证无限流
@Test
public void test8(){
Stream.iterate(1,x->x+2)
.limit(5)
.peek(num -> System.out.println("当前数:"+num))
.sorted((x,y)->y.compareTo(x))
.forEach(System.out::println);
}
/*
当前数:1
当前数:3
当前数:5
当前数:7
当前数:9
9 7 5 3 1
*/
2.3、Optional类型
Optional< T >对象是一种包装器对象,要么包装类型T的对象,要么没有包装任何对象,Java8为了解决null值判断问题,借鉴google guava类库的Optional类而引入的一个同名Optional类。java.util.Optional类可以包含或不包含null值的容器对象。
除了Optional类之外,还扩展了一些常用类型的Optional对象,比如:OptionalDouble、OptionalInt、OptionalLong,用法基本上相似。
1、创建Optinal值
of、ofNullable、empty
static < T > Optional< T > of(T value) 创建一个给定值的Optional,如果value为null抛出空指针异常
static < T > Optional< T > ofNullable(T value) 创建一个给定值的Optional,如果value为null产生一个空Optional
static< T > Optional< T > empty() 产生一个空Optional
@Test
public void test1(){
Optional<String> optional1 = Optional.of("hello_Optional");
System.out.println(optional1);//Optional[hello_Optional]
Optional<String> optional2 = Optional.ofNullable(null);
System.out.println(optional2);//Optional.empty
Optional<String> optional3 = Optional.empty();
System.out.println(optional3);//Optional.empty
}
2、获取Optional值
orElse、orElseGet、orElseThrow
T orElse(T other) 获取Optional值,如果当前Optional为空获取other
T orElseGet(Supplier<? extends T> other) 获取Optional值,如果Optional为空,获取调用other结果
< X extends Throwable > T orElseThrow(Supplier<? extends X> exceptionSupplier) 获取Optional值,如果Optional为空,抛出Supplier接口创建的异常
@Test
public void test2() throws FileNotFoundException {
Optional<String> optional= Optional.ofNullable(null);
Optional<String> helloOptional = Optional.of("hello_Optional");
// orElse产生Optional值,如果当前Optional为空产生other
System.out.println(optional.orElse("No words")); // No words
System.out.println(helloOptional.orElse("No words")); //hello_Optional
//获取Optional值,如果Optional为空,获取调用other结果
System.out.println(optional.orElseGet(() -> "Optional为空"));//Optional为空
System.out.println(helloOptional.orElseGet(() -> "Optional为空"));//hello_Optional
//获取Optional值,如果Optional为空,抛出Supplier接口创建的异常
System.out.println(helloOptional.orElseThrow(FileNotFoundException::new));//hello_Optional
System.out.println(optional.orElseThrow(FileNotFoundException::new));//抛出异常
}
ifPresent
void ifPresent(Consumer< ? super T > consumer) 如果 Optional 中有值,则对该值调用 consumer,否则什么也不做
@Test
public void test3() {
Optional<String> optional= Optional.ofNullable(null);
Optional<String> helloOptional = Optional.of("hello_Optional");
// ifPresent如果 Optional 中有值,则对该值调用 consumer,否则什么也不做
helloOptional.ifPresent(x-> System.out.println("Optional内容:"+x)); // Optional内容:hello_Optional
optional.ifPresent(x-> System.out.println("Optional内容:"+x));
}
map、filter
< U > Optional< U > map(Function<? super T, ? extends U> mapper)获取Optional值,如果存在,由给定函数mapper应用与当前Optional值而得到,否则产生一个空Optional
Optional< T > filter(Predicate<? super T> predicate) 获取Optional值,如果当前Optional满足给定predicate条件,则由满足的值构成,否则产生一个空Optional
@Test
public void test4() {
Optional<String> optional= Optional.ofNullable(null);
Optional<String> helloOptional = Optional.of("hello_Optional");
// map获取Optional值,如果存在,由给定函数mapper应用与当前Optional值而得到,否则产生一个空Optional
Optional<String> stringOptional = helloOptional.map(String::toUpperCase);
System.out.println(stringOptional.orElse("不存在")); // HELLO_OPTIONAL
Optional<Integer> integer = optional.map(String::length);
System.out.println(integer.orElse(0));// 0
//filter获取Optional值,如果当前Optional满足给定predicate条件,则由满足的值构成,否则产生一个空Optional
System.out.println(helloOptional.filter(x -> x.contains("_")).orElse("不符合"));//hello_Optional
System.out.println(helloOptional.filter(x -> x.contains("bye")).orElse("不符合"));//不符合
}
get
T get() 当Optional值存在情况下获取其中包装的元素,不存在则抛出一个NoSuchElementException异常
@Test
public void test5() {
Optional<String> optional= Optional.ofNullable(null);
Optional<String> helloOptional = Optional.of("hello_Optional");
// get当Optional值存在情况下获取其中包装的元素,不存在则抛出一个NoSuchElementException异常
System.out.println(helloOptional.get());//hello_Optional
System.out.println(optional.get());//java.util.NoSuchElementException: No value present
}
3、如何正确使用Optional类型
- Optional类型变量永远都不应该为null
- 不要使用Optional类型的域,因为会额外多出一个对象。
- 不要在集合中放置Optional对象,更不要将它们作为map的键,应该直接收集其中的值
2.4、终端操作
终端操作会从流的流水线生成结果。其结果是任何不是流的值,比如List、Integer,甚至void。
1、count、max、min方法
long count();返回流中元素数量
Optional< T > max( Comparator<? super T> comparator );
Optional< T > min(Comparator<? super T> comparator);
产生这个流的最大(max)和最小(min)元素,使用给定比较器定义的排序规则,如果流为空则得到一个空的Optional对象
@Test
public void test9(){
List<String> list = Arrays.asList("Stram", "count", "max", "min");
System.out.println(list.stream().count());// 4
list.stream()
.max(String::compareToIgnoreCase)
.ifPresent(x-> System.out.println("最大串:"+x));//最大串:Stram
list.stream()
.min(String::compareToIgnoreCase)
.ifPresent(x-> System.out.println("最小串:"+x));//最小串:count
}
2、findFirst、findAny方法
Optional< T > findFirst();
Optional< T > findAny();
分别产生第一个和任意一个元素,如果流为空则得到一个空的Optional对象,使用findAny()是为了更高效的性能。如果是数据较少,串行地情况下,一般会返回第一个结果,如果是并行的情况,那就不能确保是第一个。
@Test
public void test10(){
List<String> lst1 = Arrays.asList("Jhonny", "David", "Jack", "Duke", "Jill","Dany","Julia","Jenish","Divya");
List<String> lst2 = Arrays.asList("Jhonny", "David", "Jack", "Duke", "Jill","Dany","Julia","Jenish","Divya");
Optional<String> findFirst = lst1.parallelStream().filter(s -> s.startsWith("D")).findFirst();
Optional<String> fidnAny = lst2.parallelStream().filter(s -> s.startsWith("J")).findAny();
System.out.println(findFirst.get()); //总是打印出David
System.out.println(fidnAny.get()); //会随机地打印出Jhonny/Jack/Jill/Julia
}
3、anyMatch、allMatch、noneMatch
boolean anyMatch(Predicate<? super T> predicate);
boolean allMatch(Predicate<? super T> predicate);
boolean noneMatch(Predicate<? super T> predicate);
分别在流中任意元素、全部元素、和没有任何元素匹配给定的predicate,匹配成功返回true否则返回false
@Test
public void test11(){
List<String> list = Arrays.asList("hello", "stram", "anyMatch");
System.out.println(list.stream().anyMatch(x -> x.startsWith("s")));//匹配任意一个元素是否以s开头 true
System.out.println(list.stream().allMatch(x -> x.startsWith("s")));//匹配所有元素是否以s开头 false
System.out.println(list.stream().noneMatch(x -> x.startsWith("s")));//匹配没有元素是否以s开头 false
}
4、iterator、forEach、toArray
Iterator< T > iterator(); 产生一个获取当前各个元素的迭代器
void forEach(Consumer<? super T> action) 在流的每个元素调用action
Object[] toArray();产生一个对象数组
< A > A[] toArray(IntFunction<A[]> generator); 产生一个对象数组,将引用A[]::new传递给构造器时,返回一个A类型的数组
@Test
public void test12() {
// 1 2 3 4 5
Stream.iterate(1, x -> x + 1)
.limit(5)
.iterator().forEachRemaining(System.out::println);
// 1 2 3 4 5
Stream.iterate(1, x -> x + 1)
.limit(5)
.forEach(System.out::println);
//1 2 3 4 5
Object[] objects = Stream.iterate(1, x -> x + 1)
.limit(5)
.toArray();
for (Object object : objects) {
System.out.println(object);
}
//1 2 3 4 5
Integer[] arrays = Stream.iterate(1, x -> x + 1)
.limit(5)
.toArray(Integer[]::new);
for (Integer arr : arrays) {
System.out.println(arr);
}
}
5、collect
<R, A> R collect(Collector<? super T, A, R> collector);使用给定的收集器来收集当前流中元素,Collectors类中多种收集器工厂方法
Collectors.toList()
- static < T > Collector<T, ?, List< T >> toList() 产生一个将元素收集到List集合的收集器
Collectors.toSet()
- static < T >Collector<T, ?, Set< T >> toSet() 产生一个将元素收集到Set集合的收集器
Collectors.toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper)
- static <T, K, U> Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper) 产生一个收集器,它会产生一个映射表,keyMapper和valueMapper函数会应用于每个收集到的元素
@Test
public void test13(){
//Collectors.toList()
List<String> collect = Stream.of("hello","collect","toList")
.collect(Collectors.toList());
collect.forEach(System.out::println);
//Collectors.toSet()
Set<String> set = Stream.of("hello","collect","toSet")
.collect(Collectors.toSet());
set.forEach(System.out::println);
//Collectors.toMap(lambda,lambda)
Map<Integer, String> map = Stream.of("hello", "collect", "toMap")
.collect(Collectors.toMap(String::hashCode, String::toUpperCase));
map.entrySet().iterator().forEachRemaining(System.out::println);
}
Collectors.groupingBy(Function<? super T, ? extends K> classifier);
- static <T, K> Collector<T, ?, Map<K, List< T >>> groupingBy(Function<? super T, ? extends K> classifier) 产生一个收集器,它会产生一一个映射表map,其键是classifier应用与所有元素产生的记过,而值是具有相同键的列表(分组)
@Test
public void test14(){
List<String> list = Arrays.asList("hello","test14","Stream", "Grouping", "By","to");
Map<Integer, List<String>> collect = list.stream()
.collect(Collectors.groupingBy(String::length));
collect.entrySet().iterator().forEachRemaining(System.out::println);
/**
* 2=[By, to]
* 5=[hello]
* 6=[test14, Stream]
* 8=[Grouping]
*/
}
Collectors.partitioningBy(Predicate<? super T> predicate)
- static < T >Collector<T, ?, Map<Boolean, List< T >>> partitioningBy(Predicate<? super T> predicate) 产生一个收集器,它会产生一个映射表,其键是true/false,而值是有满足/不满足断言的元素构成的列表
@Test
public void test15(){
List<String> list = Arrays.asList("hello","test14","Stream", "Grouping", "By","to");
Map<Boolean, List<String>> collect = list.stream()
.collect(Collectors.partitioningBy(x -> x.length() > 3));
collect.entrySet().iterator().forEachRemaining(System.out::println);
/**
* false=[By, to]
* true=[hello, test14, Stream, Grouping]
*/
}
Collectors.summarizingInt(ToIntFunction<? super T> mapper)
Collectors.summarizingDouble(ToIntFunction<? super T> mapper)
Collectors.summarizingDouble(ToIntFunction<? super T> mapper)
- static < T > Collector<T, ?, IntSummaryStatistics> summarizingInt(ToIntFunction<? super T> mapper)
产生一个收集器,对将mapper应用到收集到的元素后计算总和。
@Test
public void test16(){
IntSummaryStatistics collect = Stream.of(1,2,3,4,5)
.collect(Collectors.summarizingInt(x -> x + 2));
System.out.println("最大值: "+collect.getMax());
System.out.println("最小值: "+collect.getMin());
System.out.println("平均值: "+collect.getAverage());
System.out.println("个数: "+collect.getCount());
System.out.println("总值: "+collect.getSum());
}
6、reduce
Optional< T > reduce(BinaryOperator< T > accumulator); 用给定的accumulator函数产生流中所有元素的累积总和
@Test
public void test17(){
Optional<Integer> reduce = Stream.iterate(1, x -> x + 1)
.limit(10)
.reduce((x, y) -> x + y);
System.out.println(reduce.orElse(404));// 55
}
2.5、基本类型流
若将每个整数都包装到包装器对象中这样的处理很低效,对其他基本类型情况也一样,这些基本类型是double、float、long、short、char、byte和boolean,流库中具有专门的类型IntStream、LongStream和DoubleStream,用于直接存储基本类型值,而无须使用包装器,如果想要存储short、char、byte、和boolean可以使用IntStream;而对于float可以使用DoubleStream
以IntStream为例:
static IntStream range(int startInclusive, int endExclusive)
static IntStream rangeClosed(int startInclusive, int endInclusive)
产生一个由给定范围内的整数构成的InStream
static IntStream of(int… values)
由给定元素构成的IntStream
Stream< Integer > boxed();
产生用于当前流中的元素的包装器对象
2.6、并行流
S parallel();
产生一个与当前流元素相同的并行流
@Test
public void test18(){
//并行流
Stream<Integer> stream = Stream.iterate(1, x -> x + 1).limit(10).parallel();
stream.peek((x)-> System.out.println(Thread.currentThread().getName()+" => "+x)).filter(x->x<5)
.peek((x)-> System.out.println(Thread.currentThread().getName()+" => "+x)).filter(x->x>5)
.forEach(System.out::println);
}
@Test
public void test19(){
Stream<Integer> stream = Stream.iterate(1, x -> x + 1).limit(10);
stream.peek((x)-> System.out.println(Thread.currentThread().getName()+" => "+x)).filter(x->x>2)
.peek((x)-> System.out.println(Thread.currentThread().getName()+" => "+x)).filter(x->x<8)
.forEach(System.out::println);
}
注意:
- 当使用并行流时,系统除了主线程外启动了其他个线程来执行处理任务,因此执行是无序的,但同一个线程内处理的数据是按顺序进行的
- 当我们使用顺序流时,数据按照源数据的顺序依次通过管道,当一个数据被filter过滤,或者经过整个管道而输出后,第二个数据才会开始重复这一过程
Stream< E > parallelStream()
用当前集合中的元素产生一个并行流
@Test
public void test20(){
List<String> list = Arrays.asList("hello", "Stream", "come", "over", "bye", "every", "one", "to", "hi");
list.parallelStream()
.peek((x) -> System.out.println(Thread.currentThread().getName() + " peek1=> " + x)).filter(x->x.length()>2)
.peek((x) -> System.out.println(Thread.currentThread().getName() + " peek2=> " + x)).filter(x->x.length()<5)
.forEach(System.out::println);
}