JAVA8 Stream流

Stream 是一个 Collection 的增强工具,可以对集合进行各种操作,而且可以很方便的写出并发程序,学习之前需要了解一些函数,可以看 JAVA8 Lambda表达式。常见的获取方式就是 Collection.stream()。

操作类型

操作类型分为三种

Intermediate(中间操作):可以多次使用,因为返回一个 Stream。比如 map(mapToInt, flatMap)、filter、sorted、limit、skip。

Terminal(结束操作):使用后就会结束。比如 forEach、sort、collect、min、count、findFirst、anyMatch。

中间操作都是惰性的,也就是延迟的,所以会产生副作用,关于副作用会在之后的章节详细说明。

创建

Stream 的类型有三种 IntStream,DoubleStream 和 LongStream,当然也可以使用 Stream<T>。

除了直接创建,还能通过 Collection 接口的 stream() 和 parallelStream() 创建,其中 parallelStream() 创建的流是并发、线程不安全且操作无序的,虽然它是并发的,但仍有可能在某些操作下变回串行的,例如 forEachOrdered,此外还需要保证数据源是线程安全的。

下面的代码展示了流的创建,第二行代码是取流中前三个元素在控制台输出,如果开启最后一行注释将会出现 java.lang.IllegalStateException 的异常,详细错误信息为 stream has already been operated upon or closed,所以创建的 Stream 仅能使用一次。

IntStream intStream = IntStream.of(1, 2, 3, 4); intStream.limit(3).forEach(System.out::println); //intStream.limit(1).forEach(System.out::println); 使用

JAVA8 中 Stream 接口中的操作有 filter、map、mapToInt、mapToLong、mapToDouble、flatMap、flatMapToInt、flatMapToLong、flatMapToDouble、distinct、sorted、peek、limit、skip、forEach、forEachOrdered、toArray、reduce、collect、min、max、count、anyMatch、allMatch、noneMatch、findFirst、findAny,方法很多,没见过的看注释、参数和返回值就懂了。

示例使用 List 接口 的 stream 方法创建 Stream,下面是示例所需要的数据。

//Get、Set、构造方法浪费空间,不粘贴了 public class Person { //id private Integer id; //名字 private String name; //年龄 private Integer age; //组织 private String organization; } //数据初始化 List<Person> personList = new ArrayList<>(); personList.add(new Person(1, "灰原哀", 7, "帝丹小学")); personList.add(new Person(2, "江户川柯南", 7, "帝丹小学")); personList.add(new Person(3, "宫野明美", 24, "黑衣组织")); personList.add(new Person(4, "赤井秀一", 27, "FBI")); personList.add(new Person(5, "贝尔摩德", 29, "黑衣组织"));

下面是对 Stream 操作的两个个简单示例。

第一个将 personList 中 前 4 个、年龄为 7 的人提取出来,以组织和姓名为 key 和 value 组装成一个新的 Map,其中 Collectors.toMap 方法最后一个参数用于解决 key 冲突。

第二个获取 personList 中名字为安室透的第一个人。

Map<String, String> map = personList.stream().limit(4) //中间操作 .filter(person -> Objects.equals(person.getAge(), 7)) //中间操作 .collect(Collectors.toMap( // Collectors 类是随 Stream 一起引入的,即方便又好看,作用之一是收集元素到集合 Person::getOrganization, // map 的 key Person::getName, // map 的 value (old, now) -> old)); // 发生冲突的解决办法 Optional<Person> optional = personList.stream() .filter(person->Objects.equals(person.getName(), "安室透")) //中间操作 .findFirst();

第二个方法返回值类型是 Optional<Person>,Optional 是 JAVA8 中引入的一个容器,可以使用 get() 获取容器中的值,但 optional 中并没有值,所以会抛出 java.util.NoSuchElementException,为了解决这个问题可以使用 orElse(),当容器中值为空时返回设定的默认值,除了 orElse 还有 orElseGet 和 orElseThrow。比如下面的这段代码返回了叫安室透的人。

// optional 在上一段代码中产生的对象 Person person = optional.orElse(new Person(7, "安室透", 29, "日本公安")); 副作用

对流的中间操作会产生副作用,结果是抛异常和数据的错误,它的来源有“干扰”和“有状态的 Lambda”。

“干扰”就是在中间操作时修改了流的数据源。比如在 forEach(Consumer<? super T> action) 中应该是消费数据,却给数据源添加了一个数据,结果是抛出了 java.util.ConcurrentModificationException 异常。

“有状态的 Lambda”,当后面操作产生的结果会被前面的操作影响时,前面操作的 Lambda 就被称作是有状态的,比如 的例子,例子中使用 parallelStream() 并发添加数据到 parallelStorage 中,结果就是 parallelStorage 中的数据顺序不可预测,因此称 e -> { parallelStorage.add(e); return e; } 是有状态的 Lambda。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zzwwfw.html