JDK1.8 关于stream接口的一系列流操作

java.util.stream

包汇总: java.util.stream

新的java.util.stream包提供了“支持在流上的函数式风格的值操作”(引用javadoc)的工具。

java.util.Stream 表示能应用在一组元素上一次执行的操作序列。Stream 操作分为中间操作或者最终操作两种,最终操作返回一特定类型的计算结果,而中间操作返回Stream本身,这样你就可以将多个操作依次串起来。活动流Stream 的创建需要指定一个数据源,比如 java.util.Collection的子类,List或者Set, Map不支持。

Java 8扩展了集合类,可以通过 Collection.stream() 或者 Collection.parallelStream() 来创建一个串行Stream或者并行Stream。

串行Stream如下所示:

Stream<T> stream = collection.stream();

 

一个流就像一个地带器。这些值“流过”(模拟水流)然后他们离开。一个流可以只被遍历一次,然后被丢弃。流也可以无限使用。

流Stream的操作可以是 串行执行  或者  并行执行。 它们可以使用其中一种方式开始,然后切换到另外的一种方式,使用stream.sequential()或stream.parallel()来达到这种切换。

串行Stream上的操作是在一个线程中依次完成,而并行Stream则是在多个线程上同时执行。

所以,你想用一个流来干什么?这里是在javadoc包里给出的例子:

int sumOfWeights = blocks.stream().filter(b -> b.getColor() == RED)
                                  .mapToInt(b -> b.getWeight())
                                  .sum();
 

注意,上面的代码使用了一个原始的流,以及一个只能用在原始流上的sum()方法。下面马上就会有更多关于原始流的细节:

 

流提供了流畅的API,可以进行数据转换和对结果执行某些操作。流操作既可以是“中间的”也可以是“末端的”。

  • 中间的 -中间的操作保持流打开状态,并允许后续的操作。上面例子中的filter和map方法就是中间的操作。这些操作的返回数据类型是流;它们返回当前的流以便串联更多的操作。
  • 末端的 - 末端的操作必须是对流的最终操作。当一个末端操作被调用,流被“消耗”并且不再可用。上面例子中的sum方法就是一个末端的操作。

通常,处理一个流涉及了这些步骤:

  1. 从某个源头获得一个流。
  2. 执行一个或更多的中间的操作。
  3. 执行一个末端的操作。

可能你想在一个方法中执行所有那些步骤。那样的话,你就要知道源头和流的属性,而且要可以保证它被正确的使用。你可能不想接受任意的Stream<T>实例作为你的方法的输入,因为它们可能具有你难以处理的特性,比如并行的或无限的。

 

有几个更普通的关于流操作的特性需要考虑:

  • 有状态的 - 有状态的操作给流增加了一些新的属性,比如元素的唯一性,或者元素的最大数量,或者保证元素以排序的方式被处理。这些典型的要比无状态的中间操作代价大。
  • 短路 - 短路操作潜在的允许对流的操作尽早停止,而不去检查所有的元素。这是对无限流的一个特殊设计的属性;如果对流的操作没有短路,那么代码可能永远也不会终止。

对每个Sttream方法这里有一些简短的,一般的描述。查阅javadoc获取更详尽的解释。下面给出了每个操作的重载形式的链接。

中间的操作:

  • filter 1 - 排除所有与断言不匹配的元素。
  • map 1 2 3 4 - 通过Function对元素执行一对一的转换。
  • flatMap 1 2 3 4 5 - 通过FlatMapper将每个元素转变为无或更多的元素。
  • peek 1 - 对每个遇到的元素执行一些操作。主要对调试很有用。
  • distinct 1 - 根据.equals行为排除所有重复的元素。这是一个有状态的操作。
  • sorted 1 2 - 确保流中的元素在后续的操作中,按照比较器(Comparator)决定的顺序访问。这是一个有状态的操作。
  • limit 1 - 保证后续的操作所能看到的最大数量的元素。这是一个有状态的短路的操作。
  • substream 1 2 - 确保后续的操作只能看到一个范围的(根据index)元素。像不能用于流的String.substring一样。也有两种形式,一种有一个开始索引,一种有一个结束索引。二者都是有状态的操作,有一个结束索引的形式也是一个短路的操作。

末端的操作:

  • forEach 1 - 对流中的每个元素执行一些操作。
  • toArray 1 2 - 将流中的元素倾倒入一个数组。
  • reduce 1 2 3 - 通过一个二进制操作将流中的元素合并到一起。
  • collect 1 2 - 将流中的元素倾倒入某些容器,例如一个Collection或Map.
  • min 1 - 根据一个比较器找到流中元素的最小值。
  • max 1 -根据一个比较器找到流中元素的最大值。
  • count 1 - 计算流中元素的数量。
  • anyMatch 1 - 判断流中是否至少有一个元素匹配断言。这是一个短路的操作。
  • allMatch 1 - 判断流中是否每一个元素都匹配断言。这是一个短路的操作。
  • noneMatch 1 - 判断流中是否没有一个元素匹配断言。这是一个短路的操作。
  • findFirst 1 - 查找流中的第一个元素。这是一个短路的操作。
  • findAny 1 - 查找流中的任意元素,可能对某些流要比findFirst代价低。这是一个短路的操作。

如 javadocs中提到的 , 中间的操作是延迟的(lazy)。只有末端的操作会立即开始流中元素的处理。在那个时刻,不管包含了多少中间的操作,元素会在一个传递中处理(通常,但并不总是)。(有状态的操作如sorted() 和distinct()可能需要对元素的二次传送。) 

那么接下来,简介下相关流操作的使用:

测试对象A

1 public class A {
2     private int id;
3     private String userName;
4     private String sex;
5     private String job;
6     ..... 
7     ....//省略get和set方法 
8 }

一、代替for循环

在List<A> aList中,查找userName为hanmeimei的对象A。

1、查找集合中的第一个对象——filter

aList.stream() .filter(...) .findFirst()
1  Optional<A> firstA= aList.stream() .filter(a -> "hanmeimei".equals(a.getUserName())) .findFirst();

关于Optional,java API中给了解释。 

1 //容器对象,该对象可以包含或不包含非空值。如果存在一个值,isPresent()将返回true,并且 get()将返回该值。
2 A container object which may or may not contain a non-null value. If a value is present, isPresent() will return true and get() will return the value.

所以,我们可以这样子使用:

1 if (firstA.isPresent()) {
2      A a = firstA.get();   //获取对象
3 }else {
4    //没有查到的逻辑
5 }

2、查找满足条件的对象,并返回集合——filter、collect

aList.stream() .filter(...) .collect(Collectors.toList())
List<A> firstA= aList.stream() .filter(a -> "hanmeimei".equals(a.getUserName())) .collect(Collectors.toList());

 

3、对象列表 - > 字符串列表,即:获取对象集合中所有的userName的集合。——map

  注意:stream().map( A::getXXX ) 等价于 stream().map(t -> t.getXXX)

aList.stream.map(...).collect(Collectors.toList())
1 //方法一
2 List<String> idList = aList.stream.map(A::getUserName).collect(Collectors.toList());
3 //方法二
4 List<String> collect = staff.stream().map(x -> x.getUserName()).collect(Collectors.toList());

 

4、对象集合 - > 其他对象集合 ,     此示例说明如何将 A对象集合转换为 D对象集合。

 1 public static void main(String[] args) {
 2 
 3         List<A> voList= Arrays.asList(
 4                 new A("mkyong", "男", "医生"),
 5                 new A("jack", "女", "护士"),
 6                 new A("lawrence", "女", "科学家")
 7         );
 8 
 9         // convert inside the map() method directly.
10         List<D> result = voList.stream().map(temp -> {
11             D obj = new D();
12             obj.setName(temp.getUserName());
13             obj.setSex(temp.getSex());
14             if ("mkyong".equals(temp.getUserName())) {
15                 obj.setExtra("this field is for mkyong only!");
16             }
17             return obj;
18         }).collect(Collectors.toList());
19 
20         System.out.println(result);
21 
22     }

输出结果为:
D{name='mkyong', sex="男", extra='this field is for mkyong only!'},
D{name='jack', sex="女", extra='null'},
D{name='lawrence', sex="女", extra='null'}

 二、List转Map

id为key,A对象为value,可以这么做:

1 /**
2  * List -> Map
3  * 需要注意的是:
4  * toMap 如果集合对象有重复的key,会报错Duplicate key ....
5  *  对象a1,对象a2的id都为1。
6  *  可以用 (k1,k2)->k1 来设置,如果有重复的key,则保留key1,舍弃key2
7  */
8 Map<Integer, A> aMap = aList.stream().collect(Collectors.toMap(A::getId, a -> a,(k1,k2)->k1));

更多内容稍后再补。

 

posted @ 2018-09-29 17:17  风一样的女子◕ᴗ◕。  阅读(3499)  评论(0编辑  收藏  举报