Stream API之常用操作

1 collect()

1.1 collect(Collectors.toList())

        该方法由Stream里的值生成一个列表,是一个及早求值操作。Stream的of方法使用一组初始值生成新的Stream:

List<String> collected = Stream.of("a", "b", "c").collect(Collectors.toList());
assertEquals(Arrays.asList("a", "b", "c"), collected);

1.2 collect(Collectors.joining(","))

        该方法用于将字符串进行拼接:

List<String> integers = Arrays.asList("3", "2", "5", "1", "3", "12", "4", "21", "2");
String collect = integers.stream().collect(Collectors.joining(","));

2 map

        如果有一个函数可以将一种类型的值转换成另外一种类型,map操作就可以使用该函数,将一个流中的值转换成一个新的流。

        读者可能已经注意到,以前编程时或多或少使用过类似map的操作。比如编写一段Java代码,将一组字符串转换成对应的大写形式。在一个循环中,对每个字符串调用toUppercase方法,然后将得到的结果加入一个新的列表。代码如下所示:

List<String> collected = new ArrayList<>();
for (String string : asList("a", "b", "hello")) {
    String uppercaseString = string.toUpperCase();
    collected.add(uppercaseString);
}
assertEquals(Arrays.asList("A", "B", "HELLO"), collected);

        如果你经常实现上面代码中这样的for循环,就不难猜出map是Stream上最常用的操作之一。下面展示了如何使用新的流框架将一组字符串转换成大写形式:

List<String> collected = Stream.of("a", "b", "hello").map(string -> string.toUpperCase()).collect(Collectors.toList());
assertEquals(Arrays.asList("A", "B", "HELLO"), collected);

        传给map的Lambda表达式只接受一个String类型的参数,返回一个新的String。参数和返回值不必属于同一种类型,但是Lambda表达式必须是Function接口的一个实例,Function接口是只包含一个参数的普通函数接口。


3 filter

        遍历数据并检查其中的元素时,可尝试使用Stream中提供的新方法filter。

        假设要找出一组字符串中以数字开头的字符串,比如字符串“1abc”和“abc”,其中“1abc”就是符合条件的字符串。可以使用一个for循环,内部用if条件语句判断字符串的第一个字符来解决这个问题,代码如下所示:

List<String> beginningWithNumbers = new ArrayList<>();
for (String value : asList("a", "1abc", "abc1")) {
    if (isDigit(value.charAt(0))) {
        beginningWithNumbers.add(value);
    }
}
assertEquals(Arrays.asList("1abc"), beginningWithNumbers);

        你可能已经写过许多类似的代码:这被称为filter模式。该模式的核心思想是保留Stream中的一些元素,而过滤掉其他的。下面的代码展示了如何使用函数式风格编写相同的代码:

List<String> beginningWithNumbers = Stream.of("a", "1abc", "abc1").filter(value -> isDigit(value.charAt(0))).collect(Collectors.toList());
assertEquals(Arrays.asList("1abc"), beginningWithNumbers);

        和map很像,filter接受一个函数作为参数,该函数用Lambda表达式表示。该函数和前面示例中if条件判断语句的功能一样,如果字符串首字母为数字,则返回true。若要重构遗留代码,for循环中的if条件语句就是一个很强的信号,可用filter方法替代。

        由于此方法和if条件语句的功能相同,因此其返回值肯定是true或者false。经过过滤,Stream中符合条件的,即Lambda表达式值为true的元素被保留下来。该Lambda表达式的函数接口正是之前文章中介绍过的Predicate。


4 flatMap

        flatMap方法可用Stream替换值,然后将多个Stream连接成一个Stream。    

        前面已介绍过map操作,它可用一个新的值代替Stream中的值。但有时,用户希望让map操作有点变化,生成一个新的Stream对象取而代之。用户通常不希望结果是一连串的流,此时flatMap最能派上用场。

        我们看一个简单的例子。假设有一个包含多个列表的流,现在希望得到所有数字的序列。该问题的一个解法如下面所示:

List<Integer> together = Stream.of(asList(1, 2), asList(3, 4)).flatMap(numbers -> numbers.stream()).collect(Collectors.toList());
assertEquals(Arrays.asList(1, 2, 3, 4), together);

        调用stream方法,将每个列表转换成Stream对象,其余部分由flatMap方法处理。flatMap方法的相关函数接口和map方法的一样,都是Function接口,只是方法的返回值限定为Stream类型罢了。


5 max和min

        Stream上常用的操作之一是求最大值和最小值。Stream API中的max和min操作足以解决这一问题。下面代码是查找专辑中最短曲目所用的代码,展示了如何使用max和min操作。为了方便检查程序结果是否正确,代码片段中罗列了专辑中的曲目信息:

List<Track> tracks = asList(new Track("Bakai", 524), new Track("Violets for Your Furs", 378), new Track("Time Was", 451));
Track shortestTrack = tracks.stream().min(Comparator.comparing(track -> track.getLength())).get();
assertEquals(tracks.get(1), shortestTrack);

        查找Stream中的最大或最小元素,首先要考虑的是用什么作为排序的指标。已查找专辑中的最短曲目为例,排序的指标就是曲目的长度。

        为了让Stream对象按照曲目长度进行排序,需要传给它一个Comparator对象。Java 8提供了一个新的静态方法comparing,使用它可以方便地实现一个比较器。放在以前,我们需要比较两个对象的某项属性的值,现在只需要提供一个存取方法就够了。本例中使用getLength方法。

        此外,还可以调用空Stream的max方法,返回Optional对象。Optional对象有点陌生,它代表一个可能存在也可能不存在的值。如果Stream为空,那么该值不存在,如果不为空,则该值存在。通过调用get方法可以取出Optional对象中的值。


6 reduce

        reduce操作可以实现从一组值中生成一个值。在上述例子中用到的max和min方法,因为常用而被纳入标准库中。事实上,这些方法都是reduce操作。

        下面的代码展示了如何通过reduce操作对Stream中的数字求和的过程:

int count = Stream.of(1, 2, 3).reduce(0, (acc, element) -> acc + element);
assertEquals(6, count);

        Lambda表达式就是reducer,它执行求和操作,有两个参数:传入Stream中的当前元素和acc。将两个参数相加,acc是累加器,保存着当前的累加结果。Lambda表达式的返回值是最新的acc,是上一轮acc的值和当前元素相加的结果。


7 sorted

        sorted操作用来进行排序,对于对象类型进行排序的话,需要实现Comparable接口,或者指定指定比较器:

//自然排序
List<Integer> integers = Arrays.asList(3, 2, 5, 1, 3, 12, 4, 21, 2);
integers.stream().sorted().forEach(System.out::println);

//指定比较器
List<User> list = new ArrayList<>();
list.add(User.builder().name("Emma").age(24).email("emma@qq.com").build());
list.add(User.builder().name("Joyce").age(12).email("joyce@qq.com").build());
list.add(User.builder().name("Wilson").age(28).email("wilson@qq.com").build());
list.add(User.builder().name("Tom").age(45).email("wilson@qq.com").build());
list.add(User.builder().name("Jerry").age(32).email("wilson@qq.com").build());

list.stream().sorted(Comparator.comparingInt(User::getAge)).forEach(System.out::println);

8 match

  • allMatch表示判断条件里的元素,所有的都成立则返回true;
  • anyMatch表示判断的条件里只要有一个元素成立则返回true;
  • noneMatch表示判断条件里的元素所有的都不成立则返回true。
List<User> list = new ArrayList<>();
list.add(User.builder().name("Emma").age(24).email("emma@qq.com").build());
list.add(User.builder().name("Joyce").age(12).email("joyce@qq.com").build());
list.add(User.builder().name("Wilson").age(28).email("wilson@qq.com").build());
list.add(User.builder().name("Tom").age(45).email("wilson@qq.com").build());
list.add(User.builder().name("Jerry").age(32).email("wilson@qq.com").build());

//所有人是否都大于20岁
boolean b = list.stream().allMatch(item -> item.getAge() > 20);
System.out.println(b);
//是否有人大于20岁
boolean b1 = list.stream().anyMatch(item -> item.getAge() > 20);
System.out.println(b1);
//所有人都不是20岁
boolean b2 = list.stream().noneMatch(item -> item.getAge() == 20);
System.out.println(b2);
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值