优雅地操作 List、Map
流产生的背景
集合是 Java 中使用最多的 API,但在 Java 中,集合操作却不像 SQL 语句那样简洁明了;想要用并行架构处理大量集合元素,也很困难。
为了解决这个问题,Java 8 API 中添加了新的成员 - 流(Stream)。
它允许以声明性方式(即通过类似查询语句来表达,而不是编写一个实现类)来处理数据集合;还可以不写任何多线程代码进行透明地并行处理。
流的作用与特点
作用:从支持数据处理操作的源,生成的元素序列。
特点:
流水线:很多流操作本身会返回一个流,这样多个操作就可以链接起来,形成一个大的流水线
内部迭代:与使用迭代器显式迭代的集合不同,流的迭代操作是在背后进行的
和迭代器类似,流只能遍历一次
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、IntegerforEach void Consumer<T> T -> void 消费流中的每个元素并对其应用 Lambda,返回 voidreduce 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中的一个骚操作-方法引用(使代码看起来很高大上)
- 整理了一套操作系统常见的面试题,不管你是面试大厂还是小厂都足够
- mysql从入门到优化(3)事务的基本操作
- mysql从入门到优化(1)基本操作上
- mysql从入门到优化(4)视图的基本操作
- mysql从入门到优化(2)数据的增删改查操作总结