Java8-Stream流

1、Stream简介

Java 8中的集合支持一个新的stream方法,它会返回一个流(接口定义在java.util.stream.Stream里),从支持数据处理操作的源生成的元素序列

  • 元素序列:像集合一样,流也提供了一个接口,可以访问特定元素类型的一组有序值,集合讲的是数据,流讲的是计算
  • 源:流会使用一个提供数据的源,如集合、数组或输入/输出资源
  • 数据处理操作:类似于数据库的操作,以及函数式编程语言中的常用操作,如filter、map、reduce、find、match、sort等。流操作可以顺序执行,也可并行执行。

流操作有两个重要的特点:

  • 内部迭代:流遵循“做什么而非怎么做”,流的迭代操作是在背后进行的。
  • 流水线:很多流操作本身会返回一个流,这样多个操作就可以链接起来,形成一个大的流水线。

Stream表面看起来与集合很类似,都可以让我们转换和获取数据但是它们之间存在着显著的差异:

  • Stream不存储数据。(元素可能存储在底层的集合中,或者按需生成)
  • Stream不修改数据源。(例如:filter方法不会从流中移除元素,而是会生成一个新的流,其中不包含被过滤掉的元素)
  • Stream尽可能的惰性执行。(或称之为延迟执行特性,这意味着直至需要其结果时,操作才会执行)
    在这里插入图片描述

2、使用Stream流

流的使用一般包括三件事

  1. 创建Stream流:(一个数据源(如集合)来执行一个查询)
  2. 中间操作:(指定将初始流转换为其他流,可能包含多个步骤,形成一条流的流水线)
  3. 终端操作:(执行流水线,并能生成结果)

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);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值