美文网首页Java
一起来学Java8(七)——Stream(上)

一起来学Java8(七)——Stream(上)

作者: 猿敲月下码 | 来源:发表于2019-10-31 14:46 被阅读0次

    从Java8开始,新增了一个java.util.stream包,这个包下的类和接口用来处理集合中的元素,在这个包下面有一个Stream接口,我们主要使用这个接口来对集合进行操作。

    创建Stream

    首先来看下创建Stream有哪几种方式。

    使用Stream自带的静态方法生成Stream对象,常见的静态方法有以下几个:

    • Stream.of(T)
    • Stream.of(T... values)
    • Stream.generate(Supplier)
    • Stream.iterate(T, UnaryOperator)
    • Stream.empty()

    现在来看下每个静态方法的作用

    Stream.of(T) & Stream.of(T... values)

    Stream.of是由两个重载方法组成,一个传入单值,一个传入数组

    String[] arr = {"hello", "world"};
    Stream streamArr = Stream.of(arr);
    
    String str = "hello world";
    Stream streamSingle = Stream.of(str);
    

    Stream.generate & Stream.iterate

    Stream.generate和Stream.iterate可以用来生成具有多个元素的Stream,如果不加控制会一直生成下去,一般配合limit(n)使用

    先来看下Stream.iterate

    Stream<Integer> stream5 = Stream.iterate(0,  n-> n+1)
                .limit(5);
    stream5.forEach(System.out::println);
    

    打印

    0
    1
    2
    3
    4
    

    Stream.iterate方法第一参数设定一个初始值,第二个参数表示基于这个初始值,每次循环后返回一个新的值替换这个初始值。limit(5)表示循环5次结束,最后Stream中包含了5个元素。

    再来看下Stream.generate

    Stream.generate方法只有一个Supplier参数,意思是每次循环执行Supplier接口方法返回一个新的值,放入到Stream中,由于Supplier是一个函数式接口,因此可以直接写成Lambda表达式

    AtomicInteger i = new AtomicInteger();
    Stream.generate(()-> {
        return i.getAndIncrement();
    })
    .limit(5)
    .forEach(System.out::println);
    

    上面的代码同样打印0~4。

    除了Stream静态方法之外,还可以使用Collection接口中的stream()方法来生成Stream对象。

    Collection<String> list = Arrays.asList("hello", "world");
    Stream streamList = list.stream();
    

    同理,只要是Collection接口的子类或实现类都可以使用stream()方法。

    操作Stream

    Stream中的方法有很多,大致归纳如下表格所示:

    方法 方法参数 返回类型 描述
    filer Predicate<T> Stream<T> 过滤数据
    distinct Stream<T> 去重
    map Function<T, R> Stream<R> 返回新的数据
    flatMap Function<T, R> Stream<T, Stream<R>> 返回新的数据,并做扁平化处理
    sort Comparator<T> Stream<T> 对数据进行排序操作
    limit long Stream<T> 截取前几条数据
    skip long Stream<T> 跳过几条数据
    anyMatch Predicate<T> boolean 匹配任意一条数据,如果匹配到返回true
    noneMatch Predicate<T> boolean 如果没有匹配到数据,返回true
    allMatch Predicate<T> boolean 如果所有数据全部匹配到,返回true
    findAny Optional<T> 返回任意一条数据
    findFirst Optional<T> 返回第一条数据
    count long 返回元素个数
    forEach Consumer<T> void 遍历元素,执行Consumer
    collect Collector<T, A, R> R 元素收集
    reduce BinaryOperator<T> Optional<T> 数据汇总

    从方法的返回结果可以看出,这些方法可以分为两大类,一类是返回Stream对象,可以继续对Stream操作,这类方法也被称之为中间操作(Intermediate operations),另一类是返回非Stream,结束操作,这类方法也被称之为中端操作(Terminal operations),这两类方法往往一起配合操作。

    下面我们挑选其中的几个方法来演示它们的作用。

    filter

    filter方法用来筛选出我们想要的数据,方法参数是一个Predicate接口,因为Predicate是一个函数式接口,我们可以使用Lambda表达式来写。

    Integer[] arr = { 1, 2, 3, 4, 5 };
    long count = Stream.of(arr)
            .filter(i -> i % 2 == 0)
            .count();
    System.out.println("偶数数量:" + count);
    

    在这个例子中,我们筛选出了偶数数字,并且统计出偶数的数量。如果要打印每个偶数,可以使用forEach方法

    Stream.of(arr)
            .filter(i -> i % 2 == 0)
            .forEach(System.out::println);
    

    打印:

    2
    4
    

    如果要查找任意一个元素,可以使用findAny

    int num = Stream.of(arr)
            .filter(i -> i % 2 == 0)
            .findAny()
            .orElse(0);
    System.out.println("findAny:" + num);
    

    注意,findAny()返回的是一个Optional对象,因为有可能没有找到数据,因此需要开发者自己处理没有找到数据的情况。同理findFirst也是返回一个Optional对象。

    distinct

    distinct方法会对元素进行去重操作,类似于SQL中的SELECT distinct xx

    Stream.of(1,1,2,3,3,4)
        .distinct()
        .forEach(System.out::println)
    

    打印

    1
    2
    3
    4
    

    sorted

    使用sorted方法可以对元素进行排序操作

    Stream.of(6,1,7,2,8,5)
        .sorted()
        .forEach(System.out::println);
    

    打印

    1
    2
    5
    6
    7
    8
    

    sorted()默认是从小到大排列,如果要从大到小降序,可以使用.sorted(Comparator.reverseOrder())

    Stream.of(6,1,7,2,8,5)
        .sorted(Comparator.reverseOrder())
        .forEach(System.out::println);
    

    可以看到,sorted方法允许传入一个比较器Comparator让开发者自己实现排序逻辑。下面是一个自定义Comparator例子:

    @Data
    @AllArgsConstructor
    static class Goods {
        private String goodsName;
        private int price;
    }
    
    Stream.of(
            new Goods("iphoneX", 4000)
            , new Goods("mate30 pro", 5999)
            , new Goods("redmek20", 2999)
            )
    .sorted((goods1, goods2) -> {
        return Integer.compare(goods1.getPrice(), goods2.getPrice());
    })
    .forEach(System.out::println);
    

    这个列子演示了按商品价格从低到高排序。此处的sorted部分可以简化为:.sorted(Comparator.comparing(Goods::getPrice))

    map

    map方法可以返回一个新的数据对象,组成一个新的Stream。

    List<Goods> list = Arrays.asList(
            new Goods("iphoneX", 4000)
            , new Goods("mate30 pro", 5999)
            , new Goods("redmek20", 2999)
            );
    list.stream()
        .map(goods -> goods.getGoodsName())
        .forEach(System.out::println);
    

    上面的示例演示的是从原有的商品对象中拿到商品名称,然后组成一个新的List,其效果等同于

    List<String> goodsNameList = new ArrayList<>(list.size());
    for(Goods goods : list) {
        goodsNameList.add(goods.getGoodsName());
    }
    

    map方法一般配合collect()方法一起使用

    List<Goods> list = Arrays.asList(
            new Goods("iphoneX", 4000)
            , new Goods("mate30 pro", 5999)
            , new Goods("redmek20", 2999)
    );
    List<String> nameList = list.stream()
            .map(goods -> goods.getGoodsName())
            .collect(Collectors.toList());
    

    collect(Collectors.toList())的意思是将Stream中的元素转换成List

    flatMap

    flatMap()方法是map()方法的扁平化处理,与map不同的是,flatMap把返回Stream对象操作交给开发者自己处理。看下面的例子:

    Stream<String[]> stream = Stream.of("I am Java", "hello world")
            .map(s -> s.split(" "));
    

    这个例子的本意是想要将每个字符串进行拆分,把单词单独放入到Stream中,由于map返回的是一个字符串数组String[],因此得到的Stream对象的泛型参数就是Stream<String[]>,而不是Stream<String>

    解决办法是使用flatMap:

    Stream<String> stream2 = Stream.of("I am Java", "hello world")
                    .flatMap(s -> Stream.of(s.split(" ")));
    stream2.forEach(System.out::println);
    

    打印:

    I
    am
    Java
    hello
    world
    

    综合示例

    下面来看一个综合示例,演示的功能是:查询商品名称,价格大于3000,按价格降序

    public class StreamTest3 {
        @Data
        @AllArgsConstructor
        static class Goods {
            private String goodsName;
            private int price;
        }
    
        public static void main(String[] args) {
            List<Goods> list = Arrays.asList(
                    new Goods("iphoneX", 4000)
                    , new Goods("mate30 pro", 5999)
                    , new Goods("redmek20", 2999)
            );
            // 查询商品名称,价格大于3000,按价格降序
            List<String> nameList = list.stream()
                    .filter(goods -> goods.getPrice() > 3000)
                    .sorted(Comparator.comparing(Goods::getPrice).reversed())
                    .map(Goods::getGoodsName)
                    .collect(Collectors.toList());
            System.out.println(nameList);
        }
    }
    

    打印:[mate30 pro, iphoneX]

    代码对应的SQL为:

    SELECT goods_name FROM goods WHERE price > 3000 ORDER BY price DESC
    

    小结

    本篇讲解了如何创建Stream以及Stream一些常用方法的使用方式,我们将会在下一篇着重讲解collect()reduce()的用法。

    定期分享技术干货,一起学习,一起进步!微信公众号:猿敲月下码

    相关文章

      网友评论

        本文标题:一起来学Java8(七)——Stream(上)

        本文链接:https://www.haomeiwen.com/subject/piktbctx.html