前言

上一篇博客中针对Stream流的创建做了一个简单总结,这一篇博客就简单总结一下Stream流的操作。在《Java 8 in action》一书中对Stream流的操作总结成两大类——中间操作和终端操作。

中间操作

中间操作——例如filter,sorted等即为中间操作,这些操作会返回一个流,这些流类似于一个流水生产线上的中间操作,这些操作的返回结果就是流。下面开始总结中间操作的一些实例

筛选和切片

filter

这里还是用创建Stream博客中的实例来继续讨论相关问题,这里说的筛选其实就是filter操作,如果我们要选出Dish菜单中的蔬菜,可以直接这样操作。

public static void main(String[] args) {
    List<Dish> dishList = DishContainer.getDishList();
    //filter中接收的是Predicate接口
    List<Dish> result = dishList.stream().filter(Dish::isVegetarian).collect(toList());
    System.out.println(result);
}

filter的源码如下所示:

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

其实也就是接受一个Predicate接口,满足要求的作为stream返回,返回类型没有变化,因此为中间操作。

distinct

distinct这个类似数据库中的distinct操作,其实就是去重,下面的实例可以说明distinct操作。

public static void main(String[] args) {
    List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,6,7,7,1);
    List<Integer> collect = numbers.stream().filter(i -> i % 2 == 0).distinct().collect(toList());
    System.out.println(collect);
}

运行结果:

java stream Intere 相加 java stream中间操作方法_System

结果中已经去除了重复的数据。

limit

该方法会返回一个不超过指定长度的流,也类似于SQL语句中的limit操作。

public static void main(String[] args) {
    List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,6,7,7,1);

    List<Integer> result = numbers.stream().filter(i -> i % 2 == 0).distinct().limit(2).collect(toList());
    System.out.println(result);
}

在上述代码中,加入limit(2),最终返回的结果就只有两个了

skip

skip和limit正好相反。下面的运行结果,与limit的结果互补。

public static void main(String[] args) {
    List<Integer> numbers = Arrays.asList(1,2,3,4,5,6,6,7,7,1);

    List<Integer> result = numbers.stream().filter(i -> i % 2 == 0).distinct().skip(2).collect(toList());
    System.out.println(result);
}

映射

映射相较于前面的操作,略微有点含量了。

map——对流中每一个元素应用函数

Stream流支持map方法,它会接受一个函数作为参数,这个函数会被应用到每个元素上,并将其映射成一个新的元素

例如:我们只需要获取菜单中的所有猜的名字。不需要其他信息,这个时候我们就可以用map来完成。

public static void main(String[] args) {
    List<Dish> dishes = DishContainer.getDishList();

    //map操作会映射到流中的每个元素上,一定程度上会决定返回值的类型
    List<String> dishNames = dishes.stream().map(Dish::getName).collect(toList());
    System.out.println(dishNames);
}

flatMap——流的扁平化

一个需求:给定一个单词列表,返回这个单词列表中各不相同的字符。例如,给定单词列表["Hello","World"],你想要返回列表["H","e","l","o","W","r","d"]

这个时候我们首先想到的就是利用map来完成,但是如果利用map(world->world.split("")).distinct()之后,这个返回的是一个Stream<String[]>,但是我们真正需要的是Stream<String>,这个时候可以用flatMap的方式解决它。

public static void main(String[] args) {
    String[] strings = {"hello","world"};
    List<String> streams = Arrays.stream(strings).map(word -> word.split("")).flatMap(Arrays::stream).distinct().collect(toList());
    System.out.println(streams);
}

flatMap(Arrays::stream),使用flatMap方法的效果是,各个数组并不是分别映射成一个流,而是映射成流的内容。flatMap其实就是让我们把一个流中的每个值都换成另一个流,然后把所有的流连接起来成为一个流。这里还是单独看另外一个实例吧。实例比较简单,一看就懂。

/**
 * autor:liman
 * createtime:2019/9/1
 * comment:flatmapDemo
 */
public class StreamFlagMapDemo {

    public static void main(String args[]) {

        List<String> teamIndia = Arrays.asList("Virat", "Dhoni", "Jadeja");
        List<String> teamAustralia = Arrays.asList("Warner", "Watson", "Smith");
        List<String> teamEngland = Arrays.asList("Alex", "Bell", "Broad");
        List<String> teamNewZeland = Arrays.asList("Kane", "Nathan", "Vettori");
        List<String> teamSouthAfrica = Arrays.asList("AB", "Amla", "Faf");
        List<String> teamWestIndies = Arrays.asList("Sammy", "Gayle", "Narine");
        List<String> teamSriLanka = Arrays.asList("Mahela", "Sanga", "Dilshan");
        List<String> teamPakistan = Arrays.asList("Misbah", "Afridi", "Shehzad");

        List<List<String>> playersInWorldCup2016 = new ArrayList<>();
        playersInWorldCup2016.add(teamIndia);
        playersInWorldCup2016.add(teamAustralia);
        playersInWorldCup2016.add(teamEngland);
        playersInWorldCup2016.add(teamNewZeland);
        playersInWorldCup2016.add(teamSouthAfrica);
        playersInWorldCup2016.add(teamWestIndies);
        playersInWorldCup2016.add(teamSriLanka);
        playersInWorldCup2016.add(teamPakistan);

        // 在没有java8 之前,我们针对这个集合需要遍历两次,同时需要一个集合汇总
        List<String> listOfAllPlayers = new ArrayList<>();

        for(List<String> team : playersInWorldCup2016){
            for(String name : team){
                listOfAllPlayers.add(name);
            }
        }

        System.out.println("Players playing in world cup 2016");
        System.out.println(listOfAllPlayers);


        // flatMap会将所有的集合中的集合扁平化
        List<String> flatMapList = playersInWorldCup2016.stream()
                .flatMap(pList -> pList.stream())
                .collect(Collectors.toList());

        System.out.println("List of all Players using Java 8");
        System.out.println(flatMapList);
    }
}

终端操作

终端操作会从流的流水线生成结果,其结果并不是流,例如List、Integer甚至void。从下面开始,就都是终端操作了。

查找和匹配

anyMatch

流中是否有一个元素匹配给定的条件(谓词),只要有一个匹配,就直接返回true。

List<Dish> dishes = DishContainer.getDishList();
boolean hasVegetarian = dishes.stream().anyMatch(Dish::isVegetarian);
if(hasVegetarian){
    System.out.println("the menu is vegetarian friendly;");
}

allMatch

看流中所有的元素是否匹配指定的谓词。

boolean isHealthy = dishes.stream().allMatch(d -> d.getCalories() < 1000);
if(isHealthy){
    System.out.println("the menu calories is frendly;");
}

noneMatch类似,这里就不再解释了。

findAny与findFirst

findAny——将返回当前流中的任意一个元素,findFirst——出现排序后的第一个元素。

这里直接上实例吧,异常简单。

dishes.stream().filter(Dish::isVegetarian).findFirst().ifPresent(d-> System.out.println(d.getName()));
dishes.stream().filter(Dish::isVegetarian).findAny().ifPresent(d-> System.out.println(d.getName()));

归约

归约一定程度上也是查询操作,这类查询操作需要将流中的所有元素反复结合起来,最终得到一个值。直接上实例吧,真心可以解释的不多。reduce操作,Lambda反复结合每个元素,直到流被归约成一个值。

public class StreamReduceDemo {

    public static void main(String[] args) {
        //元素求和
        Stream<Integer> stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
        Integer result = stream.reduce(0, (i, j) -> i + j);
        System.out.println(result);

        //最大值和最小值
        Stream<Integer> numStream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
        Optional<Integer> maxNum = numStream.reduce(Integer::max);
        System.out.println(maxNum.get());

        Stream<Integer> minStream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
        Optional<Integer> minNum = minStream.reduce(Integer::min);
        System.out.println(minNum.get());
        
    }
}

这里还是贴出一个reduce的详细操作,这个图直接来自《Java8 in action》

java stream Intere 相加 java stream中间操作方法_List_02

总结

总之,区分中间操作和终端操作就是看返回类型是不是一个流,其实区分这两个操作并没有那么重要。只是针对stream中的操作进行了一些简单介绍,下一篇博客会主要总结一个实例。