《Java8实战》读书笔记(二)

目录

筛选、切片和匹配

filter

distinct

limit(n)

skip(n)

map

flatmap

查找、匹配和规约

anyMatch

allMatch

noneMatch

findAny

findFirst

reduce

使用数值范围等数值流

映射到数值流

转换回对象流


这篇文章中,主要记录的是如何使用流。主要包括下面几个方面:

  • 从多个源创建流
  • 无限流

筛选、切片和匹配

filter

接受一个谓词(一个返回boolean类型的函数)作为参数,并返回一个包含所有符合谓词的元素的流。

List<Dish> vegetarianMenu = menu.stream().filter(Dish::isVagetarian).collect(toList());

distinct

返回一个元素各异(根据流所生成的元素的hashcode和equals方法实现)的流。

List<Integer> nums = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
nums.stream().filter(i -> i % 2 == 0).distinct().forEach(System.out::println);

limit(n)

返回一个不超过指定长度的流。

List<String> threeHighCaloricDishNames = menu.stream().filter(d -> d.getCalories() > 300).map(Dish::getName).limit(3).collect(toList());

skip(n)

返回一个扔掉了前n个元素的流。如果流中的元素不足n个,则返回一个空流。

List<Dish> dishs = menu.stream().filter(d -> d.getCalories() > 300).skip(2).collect(toList());

map

接受一个函数作为参数。这个函数会被应用到每一个元素上,并将其映射成新的元素(这里是创建一个新版本而不是修改)。

List<Integer> integers = menu.stream().map(Dish::getName).map(String::length).collect(toList());

flatmap

把一个流中的每一个值都换成另一个流,然后把所有的流连接起来成为一个流。

// 对于一张单词表,如何返回一张列表,列出里面各不相同的字符
String[] words = new String[] { "hello", "world" };
List<String> distinctWords = Arrays.asList(words).stream().map(word -> word.split("")).flatMap(Arrays::stream).distinct().collect(toList());
/**
     * 给定两个数字列表,如何返回所有的数对
     * 例如,给定列表[1, 2, 3]和列表[3, 4],应 该返回[(1, 3), (1, 4), (2, 3), (2, 4), (3, 3), (3, 4)]
     */
    public void numberPair() {
        List<Integer> numbers1 = Arrays.asList(1, 2, 3);
        List<Integer> numbers2 = Arrays.asList(3,4);
        
        List<int[]> list = numbers1.stream().flatMap(i -> numbers2.stream().map(j -> new int[] { i, j }))
                .collect(toList());

        System.out.println(list);
    }

查找、匹配和规约

anyMatch

流中是否有一个元素能匹配给定的谓词

    /**
     * 菜单里面是否有素食可选择
     */
    public void anyMatch() {
        if (menu.stream().anyMatch(Dish::isVagetarian)) {
            System.out.println("The menu is (somwhat) vagetarian friendly!!");
        }
    }

allMatch

流中的元素是否都能匹配给定的谓词

    /**
     * 所有的菜的热量都低于1000卡路里
     */
    public void allMatch() {
        System.out.println(menu.stream().allMatch(d -> d.getCalories() < 1000));
    }

noneMatch

流中没有任何元素与给定的谓词匹配

    /**
     * 没有菜的热量大于等于1000
     */
    public void noneMatch() {
        System.out.println(menu.stream().noneMatch(d -> d.getCalories() >= 1000));
    }

findAny

返回当前流中的任意元素。它可以与其他流操作结合使用

    /**
     * 找到一道素食菜肴
     */
    public void findAny() {
        menu.stream().filter(Dish::isVagetarian).findAny().ifPresent(d -> System.out.println(d.getName()));
    }

findFirst

找到第一个元素

    /**
     * 给定一个数字列表,找出第一个平方能被3整除的数
     */
    public void findFirst() {
        List<Integer> someNumbers = Arrays.asList(1, 2, 3, 4, 5);
        Optional<Integer> firstSquareDivisibleByThree = someNumbers.stream().map(x -> x * x).filter(x -> x % 3 == 0)
                .findFirst(); // 9
    }

reduce

归约操作(将流归约为一个值)

reduce接受两个参数:

  • 一个初始值
  • 一个BinaryOperator<T>来将两个元素结合起来产生一个新值
// 求和操作
int sum = numbers.stream().reduce(0, (a, b) -> a + b);
int sum = numbers.stream().reduce(0, Integer::sum);

// 最大值,因为没有初始值,所以返回值为Optionall<Integer>
Optional<Integer> max = numbers.stream().reduce(Integer::max);

// 最小值
Optional<Integer> min = numbers.stream().reduce(Integer::min);


// 求乘操作
int sum = numbers.stream().reduce(0, (a, b) -> a * b);

流操作:无状态和有状态

    诸如map或filter等操作会从输入流中获取每一个元素,并在输出流中得到0或1个结果。这些操作一般都是无状态的:它们没有内部状态。 

    诸如reduce、sum、max等操作需要内部状态来累积结果。不管流中有多少元素要处理,内部状态都是有界的。

    诸如sort或distinct等操作一开始和map或filter一样--都是接受一个流,再生成一个流(中间操作),但有一个关键区别。从流中排序和删除重复项时都需要知道先前的历史。例如,排序要求所有元素都放入缓冲区后才能给输出流加入一个项目,这一操作的存储要求是无界的。

使用数值范围等数值流

// 计算菜单的热量
int calories = menu.stream().map(Dish::getCalories).reduce(0, Integer::sum);

 上面这段代码的问题是它有一个暗含的装箱成本。每个Integer都必须拆箱装成一个原始类型,再进行求和。

为了解决上述的问题,Java引入了三个原始类型特化流接口。IntStream、LongStream和DoubleStream,分别将流中的元素特化成int、long、double,从而避免了暗含的装箱成本。每个接口都带来了进行常用数值归约的新方法,比如对数值流求和的sum,找到最大元素的max。

映射到数值流

将流转换为特化版本的常用方法有mapToInt、mapToDouble和mapToLong。这些方法跟map的工作方式一样,只是它们返回的是一个特化流,而不是Stream<T>。


// 如果流是空的,sum返回0 
int calories = menu.stream().mapToInt(Dish::getCalories).sum();

转换回对象流

要把原始流转换为一般流,可以使用boxed方法

IntStream intStream = menu.stream().mapToInt(Dish::getCalories);

// 将数值流转换为Stream
Stream<Integer> stream = intStream.boxed();

默认值OptionalInt

在求和时我们有一个默认值0。但是在其他的操作,比如计算IntStream流中的最大值,在这种情况下,如果返回0是错误的,因为我们没有办法区分没有元素的流和最大值真的是0的流。因此,在Java8中,用OptionalInt、OptionalDouble和OptionalLong来应对流元素为空的情况。

// 调用max方法,会返回一个OptionalInt
OptionalInt maxCalories = menu.stream().mapToInt(Dish::getCalories).max();

// 在没有最大值的情况下,默认提供一个默认值
int max = maxCalories.orElse(1);

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值