0
点赞
收藏
分享

微信扫一扫

Java8 Stream API 使用总结

Brose 2022-02-24 阅读 70

# 流的操作分类

|               |               |      |           |

| ------------- | :------------ | --------------- | :-------------- |

| 中间操作(Intermediate) | 无状态(stateless)           |   元素的处理不受之前元素的影响    | unordered(), filter(), map(),mapToInt(), mapToLong(), mapToDouble() , flatMap(), flatMapToInt() , flatMapToLong(),flatMapToDouble(), peek() |

|                          | 有状态(stateful)            |  该操作只有拿到所有元素之后才能继续下去  | distinct(),limit(),sorted(),skip                             |

| 终结操作(terminal)     | 非短路操作                  | 必须处理所有元素才能得到最终结果         | forEach(),toArray(),forEachOrdered()collect(),max(),min(),count |

|                          | 短路操作(short-circuting) | 遇到某些符合条件的元素就可以得到最终结果 | anyMatch(),noneMatch(),allMatch(),findFirst(),findAny()      |

|            |            |                |       |


1.  创建流操作

>   从一个数据源(如集合、数组)中获取一个流

2.  Intermediate(中间) 操作

> 在流后面可以跟0到N个中间操作,其存在的目的是对流的数据进行过滤和映射,然后返回一个新的流,交给下一个操作使用;这类操作都是惰性(lazy)的,也就是说,当调用到这类方法时,其实并未开始流的遍历操作。

3.  terminal(终结)操作

>   在整个流生命周期内只内进行一次,且不可逆转的操作;

>   当这类操作执行之后,流就被完全使用了,不可以再进行其他操作,所以这类操作的使用必定是最后一个流操作。terminal操作的执行,才会真正的开始流的遍历,并且产生一个结果。或者导致一个副作用(side effect)  


##  创建流


### 从集合创建流


Java8 中的 Collection 接口被扩展了,提供了两个获取流的方法:

* default Stream stream() : 返回一个顺序流

* default Stream parallelStream() : 返回一个并行流


```

List<String> list = new ArrayList<>();

Stream<String> stream = list.stream(); //获取一个顺序流

Stream<String> parallelStream = list.parallelStream(); //获取一个并行流

```


### 从数组创建流


Java8 中的 Arrays 的静态方法 stream() 可以从数组获取流:

* static Stream stream(T[] array): 返回一个流

```

String[] nums = new String[8];

Stream<String> stream = Arrays.stream(nums);

```


### 从静态方法创建流


可以使用静态方法 Stream.of(), 通过显示值创建一个流。它可以接收任意数量的参数

* public static Stream of(T… values) : 返回一个流

```

Stream<Integer> stream = Stream.of(1,2,3,4,5);


IntStream intStream = IntStream.of(new int[]{1,2,3});

IntStream.range(1,3).forEach(System.out::print);   // 不包含最大值

IntStream.rangeClosed(1,3).forEach(System.out::print);  // 包含最大值

DoubleStream doubleStream = DoubleStream.of(new int[]{1.2d,2.3d,3.4d});

```


### 从函数创建流


可以使用静态方法 Stream.iterate() 和Stream.generate(), 创建无限流。

* public static Stream iterate(final T seed, final UnaryOperator f)

* public static Stream generate(Supplier s)

```

Stream.iterate(1, (x) -> x + 1).forEach(System.out::println);

Stream.generate(() -> Math.random()).forEach(System.out::println);

```


### 流转换为其他数据结构

```

Stream stream = Stream.of("a", "b", "c");

// 转换为数组

String[] strings = (String[]) stream.toArray(String[]::new);

// 转换为集合List

List<String> list1 = (List<String>) stream.collect(Collectors.toList());

// 转换为集合List的子类ArrayList

List<String> list2 = (List<String>) stream.collect(Collectors.toCollection(ArrayList::new));

// 转换为集合Set

Set set1 = (Set) stream.collect(Collectors.toSet());

// 转换为栈

Stack stack1 = (Stack) stream.collect(Collectors.toCollection(Stack::new));

//转换为字符串

String str = stream.collect(Collectors.joining(",")).toString();

```


## Intermediate(中间操作)


在创建出流以后,我们就可以进行中间操作了。多个中间操作可以连接起来形成一个流水线,除非流水线上触发terminal(终止)操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性(lazy)求值”


中间操作按照使用功能划为又可以分为四类

*  筛选

*  切割

*  排序

*  映射



按照操作数据的状态又可以分为两类

*  有状态      -- 元素的处理不受之前元素的影响

*  无状态      -- 该操作只有拿到所有元素之后才能继续下去


### 筛选

Stream中进行筛选操作有两个方法

#### Stream<T> filter(Predicate<? super T> predicate);  

```

List<User> users = new ArrayList<>();

users.add(new User("zhangsan",18,""));

users.add(new User("lisi",26,""));

users.add(new User("米酒",40,""));

users.add(new User("郝十",18,""));


筛选年龄大于18岁的用户,并进行循环输出

users.stream()

    .filter(user->user.getAge()>18)

    .forEach(System.out::println);

```


####  Stream<T> distinct();  

根据流所生成元素的 hashCode() 和 equals() 去除重复元素

```

User user =  new User("zhangsan",18,"");

users.add(user);

users.add(new User("lisi",26,""));

users.add(new User("米酒",40,""));

users.add(new User("郝十",18,""));

users.add(user);


// 输出全部用户

users.stream().forEach(System.out::println);

// 输出去重后的全部用户

users.stream().distinct().forEach(System.out::println);

```


### 切割


Stream 中对流的切割也有两种切割方法,limit()从0开始到maxSize切割流,  skip() 从指定位置开始切割至流末尾。


#### Stream<T> limit(long maxSize);

截取元素长度不大于maxSize的元素,组成一个新的Stream.


```

Stream<Integer> integerStream = Stream.of(1,2,3,4,5,6,7,8,9,0);

integerStream.limit(5).forEach(System.out::println);

```


#### Stream<T> skip(long n);

丢弃流的前n个元素,如果流所包含的元素少于n个,那么返回一个空的stream.

```

Stream<Integer> integerStream = Stream.of(1,2,3,4,5,6,7,8,9,0);

integerStream.skip(5).forEach(System.out::println);

```


### 排序


Stream 对排序有两种方法


#### Stream<T> sorted();  

按照自然顺序排序,如果流元素不是Comparator的,则当执行Terminal操作的时候将抛出类型转换异常ava.lang.ClassCastException

```

Stream<Integer> integerStream = Stream.of(20,2,30,12,70);

integerStream.sorted().forEach(System.out::println);

```


#### Stream<T> sorted(Comparator<? super T> comparator);    

根据提供的Comparator 进行排序,源码中说,对于顺序流,排序是稳定的,对于无序流,不保证排序的稳定性


```

List<User> users = new ArrayList<>();

users.add(new User("lisi",26,""));

users.add(new User("米酒",40,""));

users.add(new User("郝十",18,""));

users.stream().sorted(Comparator.comparing(User::getAge)).forEach(System.out::println);

```


### 映射


#### <R> Stream<R> map(Function<? super T, ? extends R> mapper);

```

List<User> users = new ArrayList<>();

users.add(new User("lisi",26,""));

users.add(new User("米酒",40,""));

users.add(new User("郝十",18,""));

users.stream().map(User::getName).forEach(System.out::println);


Stream<Integer> integerStream = Stream.of(20,2,30,12,70);

integerStream.map(w->w*10).forEach(System.out::println);


```

**IntStream mapToInt(ToIntFunction<? super T> mapper);  返回一个IntStream映射**  

**LongStream mapToLong(ToLongFunction<? super T> mapper);  返回一个LongStream映射**  

**DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper); 返回一个DoubleStream映射**  

与map()方法类似,只不过返回的是对应封装类型的Stream


####  <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

多个流整合成一个流,把 input Stream 中的层级结构扁平化,就是将最底层元素抽出来放到一起,最终 output 的新 Stream 里面已经没有 List 了

```

List<Integer> list1 = Arrays.asList(1,2,3);

List<Integer> list2 = Arrays.asList(4,5,6);

List<Integer> list3 = Arrays.asList(7,8,9);

List<List<Integer>> target = new ArrayList<>();

target.add(list1);

target.add(list2);

target.add(list3);

// 原流输出

System.out.pringln("原流输出");

target.stream().forEach(System.out::println);

// 整合流之后输出

System.out.pringln("整合流输出");

target.stream().flatMap((item)-> item.stream()).forEach(System.out::println);

```

>原流输出

>[1, 2, 3]  

>[4, 5, 6]  

>[7, 8, 9]  

>整合流输出

>1  

>2  

>3  

>4  

>5  

>6  

>7  

>8  

>9  


**IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper);**  

**LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper);**  

**DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper);   **  

同map方法的其他封装类的使用方法是一样的,同样只是返回结果为封装类对应的Stream  


## 终止操作


终止操作会从流的中间操作流水线生成结果。其结果可以是任何不是流的值,例如: `List`、 `Integer`,甚至是 `void` 。


其中终止操作又分为三类


* 查找与匹配

* 规约

* 收集


### 查找与匹配


查找与匹配有很多方法,具体如下


```java

void forEach(Consumer<? super T> action);     // 内部循环流的元素

void forEachOrdered(Consumer<? super T> action);    // 内部有序循环流的元素

boolean anyMatch(Predicate<? super T> predicate);    // 任意匹配  返回boolean值

boolean allMatch(Predicate<? super T> predicate);    // 全匹配 返回boolean值

boolean noneMatch(Predicate<? super T> predicate);      // 全不匹配 返回boolean值

Optional<T> findFirst();           // 查找第一个元素

Optional<T> findAny();            // 查找任意元素

Optional<T> min(Comparator<? super T> comparator);    // 查找最小(按参数指定的规则)

Optional<T> max(Comparator<? super T> comparator);    // 查找最大元素(按参数指定的规则)

long count();             // 统计元素数量


```


#### forEach() 与forEachOrdered()的区别


```java

List<String> list = Arrays.asList("a","c","b");

list.stream().forEach(System.out::print);         // acb

System.out.println();

list.stream().forEachOrdered(System.out::print);    // acb

System.out.println();

list.parallelStream().forEach(System.out::print);   // cba

System.out.println();

list.parallelStream().forEachOrdered(System.out::print); // acb

```


串行流的情况下,两种方法都能够按照顺序进行内部循环,


但是在并行流的情况下forEach()的顺序就得不到保证了,有可能是按顺序的,有可能是任意顺序的。如上输出结果


#### anyMatch()


boolean anyMatch(Predicate<? super T> predicate);


匹配流中是否有元素与提供的谓语相匹配,如果流为空,返回false


```java

List<String> list = Arrays.asList("a","c","b");

boolean result = list.stream().anyMatch(item -> item.equals("c"));

System.out.println(result);     // true

```


代码中匹配是否含有等于"c"的元素


#### findFirst()


返回流的第一个元素的,如果流为空,则返回一个空的。如果流是无序的话,则可能返回任一元素。


Optional<T>是什么?


optional<T> 是一个可以包含为空或者非空对象的对象容器,如果对象有值,则 isPresent() 返回true,get()方法返回该对象的值。


```java

List<String> list = Arrays.asList("a","c","b");

System.out.println(list.stream().findFirst().get());  // a

```


#### count


返回此流中的元素个数


```java

List<String> list = Arrays.asList("a","c","b");

System.out.println(list.stream().count());   // 3

```


### 归约(简化)


```java

Optional<T> reduce(BinaryOperator<T> accumulator);

T reduce(T identity, BinaryOperator<T> accumulator);   // identity  初始值,

<U> U reduce(U identity,

                BiFunction<U, ? super T, U> accumulator,

                BinaryOperator<U> combiner);

```


使用实例:


```java

String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat);

System.out.println(concat);            // ABCD

Integer sum = Stream.of(1, 2, 3, 4).reduce(10, Integer::sum);

System.out.println(concat);  // 20


// 以下代码转载自:https://segmentfault.com/q/1010000004944450

//accumulator不写入list,不需要线程同步,初始值使用普通的list

List<Integer> list = new ArrayList<>();

AtomicInteger accumulateCount = new AtomicInteger(0);

AtomicInteger combineCount = new AtomicInteger(0);

List<Integer> reduceResult = IntStream.range(0, 100).parallel().boxed()

               .reduce(list, (i, j) -> {

                   accumulateCount.incrementAndGet();

                   //不改变初始的i,而是返回一个新的i

                   ArrayList<Integer> newI = new ArrayList<>(i);

                   newI.add(j);

                   return newI;

               }, (i, j) -> {

                   combineCount.incrementAndGet();

                   System.out.println(String.format("i==j: %s, thread name:%s", i == j, Thread.currentThread().getName()));

                   ArrayList<Integer> newI = new ArrayList<>(i);

                   newI.addAll(j);

                   return newI;

               });

       System.out.println("---------------------------------------");

       System.out.println("reduce result size: "+reduceResult.size());

       System.out.println("reduce result : "+reduceResult);

       System.out.println("accumulateCount: "+accumulateCount.get());

       System.out.println("combineCount: "+combineCount.get());

```


### 收集


```java

<R> R collect(Supplier<R> supplier,

                 BiConsumer<R, ? super T> accumulator,

                 BiConsumer<R, R> combiner);    // 简化调用,并发模式下结果可能会不一样

<R, A> R collect(Collector<? super T, A, R> collector);   // 高级调用

```


代码示例


```java


StringBuffer array =  Lists.newArrayList("1", "2", "3", "4", "5")

               .stream()

               .collect(StringBuffer::new, (x,y)->x.append(y), (a1, a2) -> a1.append(",").append(a2));    //  12345

```

举报

相关推荐

0 条评论