首页 文章详情

Java8新特性-说说Stream的应用

Java技术迷 | 195 2022-01-01 02:03 0 0 0
UniSMS (合一短信)

点击关注公众号,Java干货及时送达


JDK1.8新特性中较为重要的一块内容就是Stream API了,通过Stream API,我们改变了传统的集合操作,使得对集合的操作更加简单和可控。

初体验

在Stream出现以前,我们是如何对集合进行处理的呢?

比如现在有一个需求,从一个字符串集合中找出长度大于5的字符串,该如何实现呢?代码如下:

public static void main(String[] args) {
    List<String> list = Arrays.asList("apple""banana""pear""orange""peach""watermelon");
    for (int i = 0; i < list.size(); i++) {
        if (list.get(i).length() > 5) {
            System.out.println(list.get(i));
        }
    }
}

这段代码非常基础,你也可以使用增强for循环实现这一需求:

public static void main(String[] args) {
    List<String> list = Arrays.asList("apple""banana""pear""orange""peach""watermelon");
    for (String str : list) {
        if (str.length() > 5) {
            System.out.println(str);
        }
    }
}

如果要你将字符串长度大于5的字符串收集起来存入一个新的集合,你就得新建一个集合,然后在循环里进行添加:

public static void main(String[] args) {
    List<String> list = Arrays.asList("apple""banana""pear""orange""peach""watermelon");
    List<String> newList = new ArrayList<>();
    for (String str : list) {
        if (str.length() > 5) {
            newList.add(str);
        }
    }
    System.out.println(newList);
}

上述的这些操作,在Stream API中将会非常容易实现,先看一个简单的例子:

public static void main(String[] args) {
    List<String> list = Arrays.asList("apple""banana""pear""orange""peach""watermelon");
    list.forEach(System.out::println);
}

遍历一个集合变得非常简单,虽然这并不是Steam的API,但这一特性仍然是在JDK1.8之后才支持的,接下来我们就用Steam实现一下将字符串长度大于5的字符串收集成为一个新集合的需求:

public static void main(String[] args) {
    List<String> list = Arrays.asList("apple""banana""pear""orange""peach""watermelon");
    List<String> newList = list.stream().map(str -> {
        if (str.length() > 5) {
            return str;
        }
        return null;
    }).collect(Collectors.toList());
    System.out.println(newList);
}

我们首先不必纠结为什么这么写,先来看看它的效果:

[null, banana, null, orange, null, watermelon]

新集合中虽然都是长度大于5的字符串,但多了一些null值,所以,使用Stream的filter显然更适合这一需求:

public static void main(String[] args) {
    List<String> list = Arrays.asList("apple""banana""pear""orange""peach""watermelon");
    List<String> newList = list.stream().filter(str -> {
        return str.length() > 5;
    }).collect(Collectors.toList());
    System.out.println(newList);
}

运行结果:

[banana, orange, watermelon]

熟悉Lambda表达式的同学肯定能发现,这一写法还能能进行简化:

List<String> newList = list.stream().filter(str -> str.length() > 5).collect(Collectors.toList());

Stream的创建

见识到了Stream API的广大神通了吧,接下来我们回到最基本的问题,如何创建一个Stream?

public static void main(String[] args) {
    Stream<Integer> stream = Stream.of(123);
}

最简单的方式便是使用of方法,通过of方法可以创建一个由指定元素组成的Stream,但通常情况下我们并不会使用到该方法,而是使用下面的几个方法:

private static void method(List<String> list,String[] array) {
    Stream<String> listStream = list.stream();
    Stream<String> arrayStream = Arrays.stream(array);
}

Stream还支持从指定范围的数组中创建一个流(集合不支持):

private static void method(List<String> list,String[] array) {
    Stream<String> stream = Arrays.stream(array, 05);
}

创建一个不包含任何元素的流:

public static void main(String[] args) {
    Stream<Object> emptyStream = Stream.empty();
}

使用generate方法创建流:

public static void main(String[] args) {
    Stream<Double> stream = Stream.generate(Math::random);
}

创建流的方式还有很多,这里就不一一列举了。

map方法

在最初的例子中,我们用到了map和filter方法,它们的作用分别是什么以及它们的用法是如何的呢?先来所说map方法,map方法类似于遍历操作,它会得到Stream中每个元素的映射,并对其一一生效,比如:

public static void main(String[] args) {
    Stream<Integer> stream = Stream.of(12345);
    List<Integer> newList = stream.map(num -> {
        return num + 1;
    }).collect(Collectors.toList());
    System.out.println(newList);
}

这段程序的作用就是对流中的每个元素都做加1操作,运行结果如下:

[23456]

对于那些需要对集合/流中的每个元素都要做相同操作的需求,就非常适合使用map方法,比如前后端分离开发中,后端经常需要从数据库中查询出数据,然后将其封装成VO传递给前端,这一需求就能够使用map方法实现:

private static void method() {
    List<User> userList = userMapper.findAll();
    List<Object> userVOList = userList.stream().map(user -> {
        UserVO userVO = new UserVO();
        // 拷贝相同的属性
        BeanUtils.copyProperties(user, userVO);
        // 处理前端需要的额外属性
        userVO.setSex(user.getSex == 1 ? "男" : "女");
        return userVO;
    }).collect(Collectors.toList());
    System.out.println(userVOList);
}

filter方法

filter方法与map类似,它也会作用于流中的每个元素,但与其不同的是,filter方法是用来做过滤操作的,在filter中我们需要返回一个boolean类型的值,如果返回为true,则说明该值是我们需要的,如果为false,该值就会被抛弃掉,比如:

public static void main(String[] args) {
    Stream<Integer> stream = Stream.of(1357435545736);
    List<Integer> newList = stream.filter(num -> {
        return num > 30;
    }).collect(Collectors.toList());
    System.out.println(newList);
}

这段程序的作用是取出流中数值大于30的数,运行结果如下:

[35545736]

filter可以用来对流中的数据做过滤处理,比如只想要性别为男的数据;只想要工资超过2万的员工信息等等。

其它操作

Stream的神奇操作远不止这些,下面再介绍一些比较常用的功能,比如找出一个集合中的最大值:

public static void main(String[] args) {
    List<Integer> list = Arrays.asList(1123245634614);
    int max = 0;
    for (Integer num : list) {
        if(num > max){
            max = num;
        }
    }
    System.out.println(max);
}

而如果使用Stream,它将变得非常简单:

public static void main(String[] args) {
    List<Integer> list = Arrays.asList(1123245634614);
    Optional<Integer> max = list.stream().max(Comparator.naturalOrder());
    System.out.println(max.get());
}

求最小值,只需使用min方法即可:

public static void main(String[] args) {
    List<Integer> list = Arrays.asList(1123245634614);
    Optional<Integer> max = list.stream().min(Comparator.naturalOrder());
    System.out.println(max.get());
}

求和:

public static void main(String[] args) {
    List<Integer> list = Arrays.asList(1123245634614);
    Optional<Integer> sum = list.stream().reduce(Integer::sum);
    System.out.println(sum.get());
}

注意事项

在使用Strema API时需要注意的地方就是Lambda表达式的编写,如:

List<String> list = wordList.stream().filter(word -> {
    return word.startsWith("p");
list.forEach(System.out::println);

对于这样的一段程序,因为Lambda表达式体中仅包含了一条返回语句,所以表达式可以简写:

List<String> list = wordList.stream().
    filter(word -> word.startsWith("p")).collect(Collectors.toList());
list.forEach(System.out::println);

又比如:

List<Integer> numList = Arrays.asList(12345678910);
Optional<Integer> sum = numList.stream().reduce((x, y) -> {
    return x + y;
});
System.out.println(sum);

首先因为Lambda表达式体仅包含一条返回语句,所以可以简写:

Optional<Integer> sum = numList.stream().reduce((x, y) -> x + y);

我们也可以调用Integer类的静态方法sum进行求和:

Optional<Integer> sum = numList.stream().reduce((x, y) -> Integer.sum(x, y));

又因为Lambda表达式体中仅包含一条语句,且该语句调用了一个绝对存在的方法,则可以简写为:

Optional<Integer> sum = numList.stream().reduce(Integer::sum);

这就是Lambda表达式中的方法引用,具体细节可以去了解一下Lambda的相关内容。

最后就是Stream的一些其他类型,当你使用of方法创建一个流时:

Stream<Integer> stream = Stream.of(12345678910);

调用Stream类的of方法传入的是基本类型数据,但是会得到一个包装类型的Stream,我们知道,基本类型需要装箱成为包装类型,这一操作在数据量庞大的情况下是比较低效的,Stream API也考虑到这一点,所以提供了对应包装类型的Strema,如下:

IntStream intStream = IntStream.of(12'a''z');
LongStream longStream = LongStream.of(10L);
DoubleStream doubleStream = DoubleStream.of(1.1f2.2);


1、Log4j2维护者吐槽没工资还要挨骂,GO安全负责人建议开源作者向公司收费

2、太难了!让程序员崩溃的8个瞬间

3、2021年程序员们都在用的神级数据库

4、Windows重要功能被阉割,全球用户怒喷数月后微软终于悔改

5、牛逼!国产开源的远程桌面火了,只有9MB 支持自建中继器!

6、摔到老三的 Java,未来在哪?

7、真香!用 IDEA 神器看源码,效率真高!

点分享

点收藏

点点赞

点在看

good-icon 0
favorite-icon 0
收藏
回复数量: 0
    暂无评论~~
    Ctrl+Enter