Java8中新增的特性中,对核心类库的改进是很关键的一部分,这部分主要包括了集合类的API和新引入的流(Stream),流使得我们可以站在更高的抽象层次上对集合进行操作,下面我们就来熟悉一下什么是流。
Stream<String> stream1 = Stream.of("hello", "world", "hello world");
这样就创建了一个流,如果我们要找到流中字符串长度小于6的字符串个数,我们可以这样写
long count = stream1.filter(item -> item.length() < 6).count();//2
可以看到我们用了两步来求值
1.filter(item -> item.length() < 6)
过滤字符串长度小于6
2.count()
计算数量
看起来像是进行了两次循环,其实不然
stream1.filter(item -> item.length() < 6)
这行代码并未做什么实质性的工作,只是描述了Stream,我们可以测试一下
stream1.filter(item ->{
System.out.println("filter");
return item.length() < 6;
}
);
运行程序,发现并没有打印任何东西,我们继续加上count看一下
stream1.filter(item ->{
System.out.println("filter");
return item.length() < 6;
}
).count();
这时打印出了
filter
filter
filter
之所以是这样,是因为流操作分为两类:
1.惰性求值:返回一个Stream
2.及早求值:返回具体的值
一个流操作有三部分组成:
1.源
2.零个或多个中间操作
3.终止操作
在执行终止操作前,流中间的多个操作都不会运行,只有当执行了终止操作后,流才会根据所有的条件去综合执行。
常用的流操作
- collect(toList())
collect(toList())是由Stream里的值生成一个列表,是一个及早求值操作。
List<String> list = Stream.of("a", "b", "c").collect(Collectors.toList());
list.forEach(System.out::print);//abc
- map
将一个流中的值转换成一个新的流
Java8之前如果我们要将一个列表中的字符串转换成大写,要这样写
List<String> newList = new ArrayList();
List<String> list = Arrays.asList("a", "b", "c");
for (String str : list) {
newList.add(str.toUpperCase());
}
newList.forEach(System.out::print);//ABC
使用map我们只需要这样写,map所接收的Lambda表达式必须是一个Function接口的一个实例。
List<String> list = Arrays.asList("a", "b", "c");
List<String> newList = list.stream().map(String::toUpperCase).collect(Collectors.toList());
newList.forEach(System.out::print);//ABC
在Stream中对原生类型的map操作进行了几个扩展,有mapToInt、mapToLong、mapToDouble,所以在进行具体使用的时候最好具体指定具体类型的方法,这样可以避免自动装箱拆箱带来的损耗
Stream.iterate(1,item-> item+2).limit(6).filter(value -> value>200).mapToInt(value -> value * 2).skip(2).limit(2).min().ifPresent(System.out::print);
- filter
这个在最上面也讲过了,filter接收的Lambda表达式必须是一个Predicate接口的一个实例。
Stream<String> stream1 = Stream.of("hello", "world", "hello world");
long count = stream1.filter(item -> item.length() < 6).count();
System.out.println(count);
- flatMap
flatMap方法可用Stream替换值,然后将多个Stream连接成一个Stream
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
假设有一个包含多个列表的流,现在要得到所有数字的序列
Stream<List<Integer>> stream = Stream.of(Arrays.asList(1, 2, 3), Arrays.asList(4), Arrays.asList(5, 6, 7));
List<Integer> list = stream.flatMap(item->item.stream()).collect(Collectors.toList());
System.out.println(list);//[1, 2, 3, 4, 5, 6, 7]
- max和min
求最大最小值是很常用的操作,方法接收一个Comparator对象,Java8提供了一个新的静态方法comparing,该方法接收一个函数,返回一个函数,max方法返回的是一个Optional对象
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
list.stream().max(Comparator.comparing(item -> item)).ifPresent(System.out::println);//7
6.reduce
max和min都属于一种通用的编程模式,这种模式可以用以下伪代码体现:
Object accumulator = initialValue;
for(Object element : collection){
accumulator = combine(accumulator,element);
}
首先赋给accumulator一个初始值:initialValue,然后在循环体中,通过调用combine函数,拿accumulator和集合中的每一个元素做运算,再将运算结果赋给accumulator,最后accumulator的值就是想要的结果。
上面例子中用到的count、max、min方法,其实都是reduce操作,因为常用所以被纳入标准库中。
来看几个例子
求和:
int count = Arrays.asList(1, 2, 3).stream().reduce(0,(acc,item)->acc+item);
System.out.println(count);//6
求最大值:
List<Integer> list2 = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
list2.stream().reduce((maxitem,item)->{
System.out.println("maxitem:"+maxitem + "item:"+item);
if(maxitem < item){
return item;
}else{
return maxitem;
}
}).ifPresent(System.out::println);//7
log打印:
maxitem:1item:2
maxitem:2item:3
maxitem:3item:4
maxitem:4item:5
maxitem:5item:6
maxitem:6item:7
可以看出reduce执行的逻辑,上面程序是为了看内部实现,其实可以简短的写成
list2.stream().reduce(BinaryOperator.maxBy(Comparator.comparingInt(Integer::new))).ifPresent(System.out::println);
总结
本次主要介绍了Stream的基本概念以及用法,作为一个初步了解,关于Stream还有着更加复杂的操作以及一些陷阱,后续我们再逐步分析。
网友评论