集合是 Java 中使用最多的 API,但在 Java 中集合操作却不像 SQL 语句那样简洁明了;想要用并行架构处理大量集合元素,也很困难。
为了解决这个问题,Java 8 API 中添加了新的成员 - 流(Stream)。
它允许以声明性方式(即通过类似查询语句来表达,而不是编写一个实现类)处理数据集合;还可以不写任何多线程代码进行透明地并行处理。
流的作用:从支持数据处理操作的源,生成的元素序列。
特点:
Java 8 中的 java.util.Collection 接口,支持 2 个新方法:stream()、parallelStream(),返回一个流(接口定义在 java.util.stream.Stream 里)。
流的使用包括 3 个元素:
中间操作:返回另一个流,除非流水线上触发一个终端操作,否则中间操作不会执行任何处理,中间操作一般都可以合并起来,在终端操作时一次性全部处理
操作 返回类型 操作参数 函数描述符 作用
filter Stream<T> Predicate<T> T -> boolean 过滤元素
map Stream<T> Function<T, R> T -> R 映射,将元素转换成其他形式或提取信息
flatMap Stream<T> Function<T, Stream<R>> T -> Stream<R> 扁平化流映射
limit Stream<T> long 截断流,使其元素不超过给定数量
skip Stream<T> long 跳过指定数量的元素
sorted Stream<T> Comparator<T> (T, T) -> int 排序
distinct Stream<T> 去重
终端操作:从流的流水线生成结果
操作 返回类型 操作参数 函数描述符 作用
anyMatch boolean Predicate<T> T -> boolean 检查流中是否有一个元素能匹配给定的谓词
allMatch boolean Predicate<T> T -> boolean 检查谓词是否匹配所有元素
noneMatch boolean Predicate<T> T -> boolean 检查是否没有任何元素与给定的谓词匹配
findAny Optional<T> 返回当前流中的任意元素(用于并行的场景)
findFirst Optional<T> 查找第一个元素
collect R Collector<T, A, R> 把流转换成其他形式,如集合 List、Map、Integer
forEach void Consumer<T> T -> void 消费流中的每个元素并对其应用 Lambda,返回 void
reduce Optional<T> BinaryOperator<T> (T, T) -> T 归约,如:求和、最大值、最小值
count long 返回流中元素的个数
由值创建流
Stream<String> stream = Stream.of("a", "b", "c", "d");
由数组创建流
int[] numbers = {2, 3, 5, 7, 11, 13};
int sum = Arrays.stream(numbers).sum();
由文件生成流
long uniqueWords = 0;
Stream<String> lines = Files.lines(Paths.get("a.txt"), Charset.defaultCharset())){
uniqueWords = lines.flatMap(line -> Arrays.stream(line.split(" ")))
.distinct()
.count();
由函数生成流,创建无限流
Stream.iterate(0, n -> n + 2).limit(10).forEach(System.out::println);
Stream.generate(Math::random).limit(5).forEach(System.out::println);
为了避免了暗含的装箱成本,Java 8 还是推出了IntStream、DoubleStream和 LongStream,分别将流中的元素特化为int、long和double,将原始类型流特化。
映射到数值流
double weight = list.stream()
.mapToDouble(Fruit::getWeight)
.sum();
转换回对象流
DoubleStream doubleStream = list.stream().mapToDouble(Fruit::getWeight);
Streaing<Double> stream = doubleStream.boxed();
数值范围流,产生 1-100 之间的偶数
IntStream evenNumbers = IntStream.rangeClosed(1, 100)
.filter(n -> n % 2 == 0);
package constxiong.interview;
import static java.util.Comparator.comparing;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.OptionalDouble;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* 测试 Stream 的API
* @author ConstXiong
*/
public class TestStreamAPI {
public static void main(String[] args) {
List<Fruit> list = Arrays.asList(new Fruit("苹果", "红色", 1.1),
new Fruit("苹果", "绿色", 1.7),
new Fruit("香蕉", "黄色", 0.8),
new Fruit("香蕉", "绿色", 1.1),
new Fruit("橘子", "橙色", 0.7));
//获取颜色为绿色的水果的类别,按照重量排序
System.out.println(list.stream()
.filter(f -> "绿色".equals(f.getColor()))
.sorted(comparing(Fruit::getWeight))
.map(Fruit::getType)
.collect(toList()));
//利用多核架构并行执行这段代码,你只需要把stream()换成parallelStream()
//获取颜色为绿色的水果的类别,按照重量排序
System.out.println(list.parallelStream()
.filter(f -> "绿色".equals(f.getColor()))
.sorted(comparing(Fruit::getWeight))
.map(Fruit::getType)
.collect(toList()));
//打印流的执行过程
list.parallelStream()
.filter(f -> {
System.out.println("filter:" + f);
return "绿色".equals(f.getColor());
})
.map(f -> {
System.out.println("map:" + f);
return f.getType();
})
.collect(toList());
//按重量过滤,排序,取前 2 个水果
System.out.println(list.stream()
.filter(f -> f.getWeight() > 1)
.sorted(comparing(Fruit::getWeight))
.limit(2)
.collect(toList()));
//按水果类型分组
Map<String, List<Fruit>> map = list.stream().collect(groupingBy(Fruit::getType));
System.out.println(map);
//打印水果重量合计
double weight = list.stream()
.mapToDouble(Fruit::getWeight)
.sum();
System.out.println(weight);
//转换回对象流
DoubleStream doubleStream = list.stream().mapToDouble(Fruit::getWeight);
Stream<Double> stream = doubleStream.boxed(); //包装成Double流
//打印水果的最重重量
OptionalDouble maxWeight = list.stream()
.mapToDouble(Fruit::getWeight)
.max();
System.out.println(maxWeight.getAsDouble());
//数值范围流,产生 1-100 之间的偶数
IntStream evenNumbers = IntStream.rangeClosed(1, 100)
.filter(n -> n % 2 == 0);
evenNumbers.forEach(System.out::println);
}
}
参考:《Java8实战》
ConstXiong 备案号:苏ICP备16009629号-3