您的位置:首页 > 编程语言 > Java开发

JAVA IO流——IBM之Java 8 中的 Streams API 详解

2016-07-17 21:55 726 查看
Java 8 中的新特性Stream API

Stream 总览

什么是流

Stream 不是集合元素,它不是数据结构并不保存数据,它是有关算法和计算的,它更像一个高级版本的 Iterator。

Stream :是对专注于对集合对象进行各种非常便利、高效的

    1)聚合操作(aggregate operation)

    2)大批量数据操作 (bulk data operation)。

**聚合操作什么意思。除了Stream还有对集合进行操作的IO类么

    利用Lambda 表达式。Java IO流的Lambda 表达式 。

    3)提供串行、并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。

    Java 8 中首次出现的 java.util.stream 是一个函数式语言+多核时代综合影响的产物。

    [Stream 可以并行化操作,迭代器只能命令式地、串行化操作。顾名思义,当使用串行方式去遍历时,每个 item 读完后再读下一个 item。]

    [并行去遍历时,数据会被分成多个段,其中每一个都在不同的线程中处理,然后将结果一起输出。Stream 的并行操作依赖于 Java7 中引入的 Fork/Join 框架(JSR166y)来拆分任务和加速处理过程]

知识点一。在java.util.Collection接口中添加了如下方法:

Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}

Map接口中并没有stream()方法,但是Map的values()和keySet()均返回集合对象,在集合对象上当然是可以使用stream()方法的。

聚合操作实例

为说明聚合操作的使用,首先定义一个数据元素类Person,如下:

import java.time.LocalDate;

public class Person {
String name;
LocalDate birthday;
Sex gender;
String emailAddress;

public int getAge() {
return LocalDate.now().getYear() - birthday.getYear();
}

public void setBirthday(LocalDate birthday){
this.birthday = birthday;
}

public void setGender(Sex sex){
this.gender = sex;
}

public void printPerson() {
System.out.println("The name is " + name);
}

public Sex getGender(){
return gender;
}

public enum Sex {
MALE, FEMALE
}
}

在Java 8以前的版本中,对Person集合的遍历往往采用以下方式:

Set<Person> persons = new HashSet<Person>();
//传统遍历方式 for (Person person : persons) { if (person.getAge() > 18) { System.out.println(person.name + " is elder than 18."); } }

同样的功能,在Java 8中使用聚合操作,可以实现如下:

//使用聚合操作
persons.stream().filter(new Predicate<Person>() {
@Override
public boolean test(Person person) {
if (person.getAge() > 18) {
return true;
} else {
return false;
}
}
}).forEach(new Consumer<Person>() {
@Override
public void accept(Person person) {
System.out.println(person.name + " is elder than 18.");
}
});
清单 1. Java 7 的排序、取值实现

List<Transaction> groceryTransactions = new Arraylist<>();
for(Transaction t: transactions){
if(t.getType() == Transaction.GROCERY){
groceryTransactions.add(t);
}
}
Collections.sort(groceryTransactions, new Comparator(){
public int compare(Transaction t1, Transaction t2){
return t2.getValue().compareTo(t1.getValue());
}
});
List<Integer> transactionIds = new ArrayList<>();
for(Transaction t: groceryTransactions){
transactionsIds.add(t.getId());
}
而在 Java 8 使用 Stream,代码更加简洁易读;而且使用并发模式,程序执行速度更快。

清单 2. Java 8 的排序、取值实现

List<Integer> transactionsIds = transactions.parallelStream().
filter(t -> t.getType() == Transaction.GROCERY).
sorted(comparing(Transaction::getValue).reversed()).
map(Transaction::getId).
collect(toList());


流的构成

当我们使用一个流的时候,通常包括三个基本步骤:

获取一个数据源(source)→ 数据转换→执行操作获取想要的结果,每次转换原有 Stream 对象不改变,返回一个新的 Stream 对象(可以有多次转换),这就允许对其操作可以像链条一样排列,变成一个管道,如下图所示。



有多种方式生成 Stream Source:

从 Collection 和数组

Collection.stream()

Collection.parallelStream()

Arrays.stream(T array) or Stream.of()

从 BufferedReader

java.io.BufferedReader.lines()

静态工厂

java.util.stream.IntStream.range()

java.nio.file.Files.walk()

自己构建

java.util.Spliterator

其它

Random.ints()

BitSet.stream()

Pattern.splitAsStream(java.lang.CharSequence)

JarFile.stream()

流的操作类型分为两种:

Intermediate:一个流可以后面跟随零个或多个 intermediate 操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。

Terminal:一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个 side effect。

还有一种操作被称为 short-circuiting。用以指:

对于一个 intermediate 操作,如果它接受的是一个无限大(infinite/unbounded)的 Stream,但返回一个有限的新 Stream。

对于一个 terminal 操作,如果它接受的是一个无限大的 Stream,但能在有限的时间计算出结果。

当操作一个无限大的 Stream,而又希望在有限时间内完成操作,则在管道内拥有一个 short-circuiting 操作是必要非充分条件。

清单 3. 一个流操作的示例

int sum = widgets.stream()
.filter(w -> w.getColor() == RED)
.mapToInt(w -> w.getWeight())
.sum();


stream() 获取当前小物件的 source,filter 和 mapToInt 为 intermediate 操作,进行数据筛选和转换,最后一个 sum() 为 terminal 操作,对符合条件的全部小物件作重量求和。

回页首

流的使用详解

简单说,对 Stream 的使用就是实现一个 filter-map-reduce 过程,产生一个最终结果,或者导致一个副作用(side effect)。

流的构造与转换

下面提供最常见的几种构造 Stream 的样例。

清单 4. 构造流的几种常见方法

// 1. Individual values
Stream stream = Stream.of("a", "b", "c");
// 2. Arrays
String [] strArray = new String[] {"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);
// 3. Collections
List<String> list = Arrays.asList(strArray);
stream = list.stream();


需要注意的是,对于基本数值型,目前有三种对应的包装类型 Stream:

IntStream、LongStream、DoubleStream。当然我们也可以用 Stream<Integer>、Stream<Long> >、Stream<Double>,但是 boxing 和 unboxing 会很耗时,所以特别为这三种基本数值型提供了对应的 Stream。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: