Java8-12-stream 使用流

本系列文章来自CSDN

原文地址:

https://blog.csdn.net/ryo1060732496/article/details/88806298

StreamAPI支持的许多操作,这些操作能让你快速完成复杂的数据查询,如筛选、切片、映射、查找、匹配和归约。

切片和筛选

1. 用谓词筛选(filter)
Streams 接口支持 filter方法。该操作会接收一个谓词(一个返回boolean 的函数)作为参数,并返回一个包括所有符合谓词的元素的流。

List<Dish> vegetarianDishes = menu.stream()
                        //方法引用检查菜肴是否适合素食者
                        .filter(Dish::isVegetarian)
                        .collect(toList());

2. 筛选各异的元素(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);

3. 截短流(limit)
流支持 limit(n) 方法,该方法会返回一个不超过给定长度的流。

// 选出热量超过300卡路里的头三道菜
List<Dish> dishes = menu.stream()
                .filter(d -> d.getCalories() > 300)
                .limit(3)
                .collect(toList());

4. 跳过元素(skip)
流还支持 skip(n) 方法,返回一个扔掉了前n个元素的流。如果流中元素不足n个,则返回一个空流。请注意,limit(n)和skip(n)是互补的!

// 跳过超过300卡路里的头两道菜,并返回剩下的
List<Dish> dishes = menu.stream()
                .filter(d -> d.getCalories() > 300)
                // 跳过前两个
                .skip(2)
                .collect(toList());
dishes.forEach(dish -> System.out.println(dish.getName()));

映射

一个非常常见的数据处理套路就是从某些对象中选择信息。比如在SQL里,你可以从表中选择一列。Stream API也通过 map 和 flatMap 方法提供了类似的工具。

1. 对流中每一个元素应用函数(map)
流支持 map 方法,它会接受一个函数作为参数。这个函数会被应用到每个元素上,并将其映射成一个新的元素(使用映射一词,是因为它和转换类似,但其中的细微差别在于它是“创建一个新版本”而不是去“修改”)。

// 获取菜名
List<String> dishNames = menu.stream()
                .map(Dish::getName)
                .collect(toList());
System.out.println(dishNames);

// 获取菜名长度
List<Integer> len = menu.stream()
                .map(dish -> dish.getName().length())
                .collect(toList());
System.out.println(len);

2. 流的扁平化(flatMap)
对于一张单词表,如何返回一张列表,列出里面各不相同的字符呢?例如, 给定单词列表[“Hello”,“World”],你想要返回列表 [“H”,“e”,“l”, “o”,“W”,“r”,“d”]

List<String> words = Arrays.asList("Hello", "World");
List<String[]> wordList = words.stream()
        .map(word -> word.split(""))
        .distinct()
        .collect(Collectors.toList());
wordList.forEach(wordArray -> {
    
    
    for (String s : wordArray) {
    
    
        System.out.print(s);
    }
    System.out.println();
});

执行结果

Hello
World

执行完后一看,不对呀。仔细想一想:我们把[“Hello”,“World”]这两个单词把它们分割称为了字符数组,[“H”,“e”,“l”,“l”,“o”],[“W”,“o”,“r”,“l”,“d”]。然后将这个字符数组去判断是否重复,不是一个字符是否重复,而是这一个字符数组是否有重复。所以,打印出来就是Hello World。

flatMap引入

我们需要一个字符流,而不是数组流。有一个叫作Arrays.stream()的方法可以接收一个数组并产生一个流。

String[] arrayOfWords = {
    
    "Hello", "World"};
Stream<String> streamOfwords = Arrays.stream(arrayOfWords);

stream.forEach(System.out::println);

结果:

Hello
World

使用map和Arrays.stream(),显然是不行的。这是因为,你现在得到的是一个流的列表(更准确地说是Stream)。的确,你先是把每个单词转换成一个字母数组,然后把每个数组变成了一个独立的流。

使用flatMap
String[] arrayOfWords = {
    
    "Hello", "World"};
        // 将数组转化为流,此时应该是两个字符串数组流:["Hello","World"]
        List<String> uniqueCharacters = Arrays.stream(arrayOfWords)
                // 拆分为字符数组流,此时应该是两个字符数组流:["H","e","l","l","o"],["W","o","r","l","d"]
                .map(w -> w.split(""))
                // 把两个字符数组流扁平化为一个流:["H","e","l","l","o","W","o","r","l","d"]
                .flatMap(Arrays::stream)
                // 去重
                .distinct()
                // 终端操作
                .collect(Collectors.toList());
        // HeloWrd
        uniqueCharacters.forEach(System.out::print);

使用flatMap方法的效果是,各个数组并不是分别映射成为一个流,而是映射成流的内容。所有使用 map(s -> split("")) 时生成的单个流都被合并起来,即扁平化为一个流。
一言以蔽之, flatMap 方法让你把一个流中的每个值都换成另一个流,然后把所有的流连接起来成为一个流。

匹配元素

另一个常见的数据处理套路是看看数据集中的某些元素是否匹配一个给定的属性。StreamAPI通过 allMatch 、 anyMatch 、 noneMatch 、 findFirst 和 findAny 方法提供了这样的工具。

1. 检查谓词是否至少匹配一个元素(anyMatch)
anyMatch 方法可以回答“流中是否有一个元素能匹配给定的谓词”。

// 菜单里面是否有素食可选择
if(menu.stream().anyMatch(Dish::isVegetarian)){
    
    
    System.out.println("有素菜,不用担心!");
}

anyMatch 方法返回一个 boolean,因此是一个终端操作。

2. 检查谓词是否匹配所有元素(allMatch)
allMatch 方法可以回答流中的元素是否都能匹配给定的谓词。

// 菜品是否有利健康(即所有菜的热量都低于1000卡路里)
boolean isHealthy = menu.stream().allMatch(d -> d.getCalories() < 1000);

3. 确保流中没有任何匹配元素(noneMatch)
noneMatch 方法确保流中没有任何元素与给定的谓词匹配。

boolean isHealthy = menu.stream().noneMatch(d -> d.getCalories() >= 1000);

anyMatch、allMatch 和 noneMatch 这三个操作都用到了我们所谓的短路,这就是大家熟悉的 Java 中 && 和 || 运算符短路在流中的版本。

查找元素

1. 当前流中的任意元素

Optional<Dish> dish = menu.stream()
                .filter(Dish::isVegetarian)
                .findAny();

返回的是一个Optional。Optional类(java.util.Optional)是一个容器类,代表一个值存在或者不存在。

2. 查找第一个元素

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

归约

1. 元素求和
你可以像下面这样对流中所有的元素求和:

List<Integer> numbers = Arrays.asList(3, 4, 5, 1, 2);
int sum = numbers.stream().reduce(0, (a, b) -> a + b);
// 15
System.out.println(sum);

// 元素相乘
numbers.stream().reduce(1, (a, b) -> a * b);

2. 与方法引用结合

int sum2 = numbers.stream().reduce(0, Integer::sum);

3. 无初始值
reduce 还有一个重载的变体,它不接受初始值,但是会返回一个 Optional 对象。

Optional<Integer> sum = numbers.stream().reduce((a, b) -> (a + b));

考虑流中没有任何元素的情况。reduce操作无法返回其和,因为它没有初始值。这就是为什么结果被包裹在一个 Optional 对象里,以表明和可能不存在。

4. 最大值和最小值

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

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

reduce 操作会考虑新值和流中下一个元素,并产生一个新的最大值,直到整个流消耗完!

猜你喜欢

转载自blog.csdn.net/weixin_43298913/article/details/106121476
今日推荐