《Java8实战》 - 读书笔记 - Stream流的基本用法

Posted 笑虾

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《Java8实战》 - 读书笔记 - Stream流的基本用法相关的知识,希望对你有一定的参考价值。

第4章 引入流

  • 流是“从支持数据处理操作的源生成的一系列元素”
  • 流利用内部迭代:迭代通过filtermapsorted 等操作被抽象掉了。
  • 流操作有两类:中间操作终端操作
    • filtermap等中间操作会返回一个流,并可以链接在一起。可以用它们来设置一条流水线,但并不会生成任何结果
    • forEachcount等终端操作会返回一个非流的值,并处理流水线以返回结果
  • 流中的元素是按需计算的。
  • Stream 对应的有一套基础类型优化版如:IntStreamDoubleStreamLongStream 与这对应的各种操作也同理。详见:底部参考资料

第5章 使用流

表5-1 中间操作和终端操作

操作类型返回类型使用的类型/函数式接口函数描述符
filter中间Stream<T>Predicate<T>T -> boolean
distinct中间 (有状态-无界)Stream<T>
skip中间 (有状态-有界)Stream<T>long
limit中间 (有状态-有界)Stream<T>long
map中间Stream<R>Function<T, R>T -> R
flatMap中间Stream<R>Function<T, Stream<R>>T -> Stream<R>
sorted中间 (有状态-无界)Stream<T>Comparator<T>(T, T) -> int
anyMatch终端booleanPredicate<T>T -> boolean
noneMatch终端booleanPredicate<T>T -> boolean
allMatch终端booleanPredicate<T>T -> boolean
findAny终端Optional<T>
findFirst终端Optional<T>
forEach终端voidConsumer<T>T -> void
collect终端RCollector<T, A, R>
reduce终端 (有状态-有界)Optional<T>BinaryOperator<T>(T, T) -> T
count终端long

测试数据

@Data
@AllArgsConstructor
public class Hero
    private String name;
    private Integer age;
    private String group;

    public static List<Hero> getList()
        return Arrays.asList(
                new Hero("张三", 23, "A"), new Hero("李四", 19, "A"), new Hero("王五", 20, "B"),
                new Hero("赵六", 21, "B"), new Hero("洪七", 25, "C"), new Hero("重八", 45, "C")
        );
    

5.1 筛选和切片

筛选 filter

Stream<T> filter(Predicate<? super T> predicate);

List<Hero> heroes = Hero.getList().stream()
        .filter(hero -> hero.getAge() > 20) // 取年龄大于 20 的
        .collect(Collectors.toList());

去重 distinct

List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream()
        .filter(i -> i % 2 == 0) // 取偶数
        .distinct() // 去重
        .forEach(System.out::println);

截短 filter

List<Hero> heroes = Hero.getList().stream()
        .filter(hero -> hero.getAge() > 20)
        .limit(3) // 取前 3 个
        .collect(toList());
heroes.forEach(System.out::println);

跳过 skip

先丢弃前 3 个,在剩下的结果中保留前 2 个
如果想保留后两个,可以配合排序+反转

Hero.getList().stream()
        .skip(3) // 跳过前 3 个
        .limit(2) // 保留前 2 个
        .collect(toList())
        .forEach(System.out::println);

5.2 映射

映射 map

<R> Stream<R> map(Function<? super T, ? extends R> mapper);

  • 处理中每一个元素。(元素从 Hero 变成了 HeroAge
  • map 还有几个针对基础类型的优化版,直接返回基础类型流。(免于拆箱)

函数引用写法:

Hero.getList().stream()
        .map(Hero::getAge)
        .forEach(System.out::println);

Lambda写法:如果还想对年龄处理一下,可以这样

Hero.getList().stream()
         .map(hero -> hero.getAge() + "岁")
         .forEach(System.out::println);

躺平映射 flatMap

  • map:取出每个名字,拆分成数组。相当于得到一个二维数组。如:[[张, 三], [李, 四], [王, 五]]
  • Arrays::stream:接收数组创建流。
  • flatMap:将流中所有元素平铺开,放在同一个流里。如:[张, 三, 李, 四, 王, 五]
Hero.getList().stream()
        .map(hero -> hero.getName().split(""))
        .flatMap(Arrays::stream)        
        .forEach(System.out::println);

5.3 查找和匹配

查找 findAny, findFirst

这些方法利用了短路。不会傻傻的处理整个流。

方法说明
Optional<T> findFirst()返回第一个元素。
Optional<T> findAny()返回任意一个元素。

匹配 anyMatch, allMatch, noneMatch

方法说明
boolean anyMatch(Lambda)只要有一个匹配就为
boolean allMatch(Lambda)所有都匹配就为
boolean noneMatch(Lambda)所有都不匹配就为

5.4 归约

两参数版本 reduce(0, (a, c) -> a + c)

T reduce(T identity, BinaryOperator<T> accumulator);

  • 参数一:初始值。
  • 参数二:Lambda。(累计值, 当前值) -> 累计值 + 当前值
Integer sum = Hero.getList().stream()
        .map(Hero::getAge)
        .reduce(0, (a, c) -> a + c);
System.out.println("所有人加起来 " + sum + " 岁了!");

单参数版本 reduce((a, c) -> a + c)

Optional<T> reduce(BinaryOperator<T> accumulator);
默认以流中第一元素初始值

Optional<Integer> sum = Hero.getList().stream()
        .map(Hero::getAge)
        .reduce((a, c) -> a + c);
System.out.println("所有人加起来 " + sum.orElse(999) + " 岁了!");

5.5 付诸实践

略。。。

5.6 数值流

int类型数值流 mapToInt , mapToDouble, mapToLong

Double、Long 同理

映射数值流
mapToIntIntStream
mapToDoubleDoubleStream
mapToLongLongStream

转换回对象流 intStream.boxed

IntStream intStream = menu.stream().mapToInt(Dish::getCalories); 
Stream<Integer> stream = intStream.boxed();

默认值 optionalInt.orElse

如果为 null 则返回 999

OptionalInt sum = Hero.getList().stream()
        .mapToInt(Hero::getAge)
        .reduce((a, c) -> a + c);
System.out.println("所有人加起来 " + sum.orElse(999) + " 岁了!");

生成范围值 range(开始, 结束), rangeClosed(开始, 结束)

方法开始结束
public static IntStream range(int startInclusive, int endExclusive)包含不包含
public static IntStream rangeClosed(int startInclusive, int endInclusive)包含包含
IntStream intStream = IntStream.rangeClosed(1, 100);
intStream.forEach(System.out::println);

5.7 构建流

从值创建流 Stream.of

Stream.of("Java 8 ", "Lambdas ", "In ", "Action")
        .map(String::toUpperCase)
        .forEach(System.out::println);

从数组创建流 Arrays.stream

int[] numbers = 2, 3, 5, 7, 11, 13;
int sum = Arrays.stream(numbers).sum();

从文件生成流 Files.lines

java.nio.file.Files中的很多静态方法都会返回一个流。

try(Stream<String> lines = Files.lines(Paths.get("E:\\\\test.txt"), Charset.defaultCharset()))
    lines.forEach(System.out::println);

catch(IOException e) 

由函数生成无限流

无限流通常配合limit截断了来用。

迭代 Stream.iterate

接受一个初始值(在这里是0),还有一个依次应用在每个产生的新值上的Lambda(UnaryOperator类型)。

Stream.iterate(0, n -> n + 2)
	.limit(10)
	.forEach(System.out::println);

生成 Stream.generate

与iterate方法类似,generate方法也可让你按需生成一个无限流。但generate不是依次对每个新生成的值应用函数的。它接受一个Supplier类型的Lambda提供新的值。

Stream.generate(Math::random) 
	.limit(5) 
	.forEach(System.out::println);

参考资料

java.util.stream.Stream
java.util.stream.IntStream
java.util.stream.DoubleStream
java.util.stream.LongStream
java.nio.file.Files

以上是关于《Java8实战》 - 读书笔记 - Stream流的基本用法的主要内容,如果未能解决你的问题,请参考以下文章

《Java8实战》 - 读书笔记 - Stream流操作2:用流收集数据

《Java8实战》读书笔记13:Java8 与 Scala

《Java8实战》读书笔记13:Java8 与 Scala

《Java8实战》读书笔记07:Lambda 重构测试和调试(设计模式实现)

《Java8实战》读书笔记07:Lambda 重构测试和调试(设计模式实现)

《Java8实战》读书笔记11:Java8中新的日期时间API