文章

Java:聚集操作

你使用集合的目的是什么?当然不是为了简单的存储然后置之不理,你是为了从集合取数据和操作数据的。

再次考虑前一篇文章提到的背包类,如果要打印所有背包的重量,

for(Package p : packages)
System.out.println(p.getWeight());

遍历,可以使用"forEach"这一聚集操作,

packages.stream()
.forEach(e -> System.out.println(p.getWeigh());

看不懂语法不要紧。我们再来个稍微复杂一点的,我们只打印“重量大于20的背包重量”,

packages.stream()
.filter(e -> e.getWeight() > 20)
.forEach(e - > System.out.println(e.getWeight());

分析其结构,

packages是个集合,stream()方法是获取其“流”,能产生流的不只是集合,数组,I/O通道都可以。下面就是管道操作(和MongoDB或是Linux下的管道类似)了。即有0个或若干个中间操作,如这里的filter,来产生新的流,然后有一个终结操作,它可以有返回值(不再是一个流),也可以没有返回值。

其中,filter的函数原型是:

Stream<T> filter<Predicate<? super T> p)

接受一个谓词参数(条件)。

查看java.util.strem下面的API,其操作时很多的,部分如下,

Java:聚集操作

具体用法不一一介绍。

我们再看一个需求,求“重量大于20的背包的平均重量”。

除了filter表示过滤,还有maptoInt,返回一个IntStream。函数原型是,

IntStream maptoInt(toIntFunction<? super T> mapper)

其参数是一个返回Int的lambda表达式。

average()是IntStream的方法,很明显,是取平均值的,返回一个OptionalDouble,你可以回想OptionalDouble是什么?怎么不直接返回Double!!OptionalDouble是java8新加的,类似的API很多,它的特点是该对象可能包含也可能不包含一个值。作用在于:传统方法返回一个为Null的值,继续操作会报NullException,如果OptionalXX没有值,是可以继续操作的(当然没有值getAsDouble会报NoSuchElement异常。

另外,average这个终结操作也是reduction 操作。

在JDK里面,像average,sum,min等都是通过组合流的内容返回一个值,这些操作称为reduction操作。当然,有点reduction操作返回一个集合而不是一个值。值得一提的是,JDK还提供了两个一般化的操作:reduce和collect。

假设我们需要对背包容量求和,

Integer total = packages
.stream()
.mapToInt(e -> e,getWeight())
.sum();

如果要用reduce对它改造,

Integer total = packages
.stream()
.mapToInt(e -> e,getWeight())
.reduce(0,(a,b) -> a + b);

如果使用函数引用,

Integer total = packages
.stream()
.mapToInt(e -> e,getWeight())
.reduce(0,Integer::sum);

查看reduce的函数原型:

T reduce(T indentidy,BinaryOperator<T> accumulator)

这可能比较费解,API是这样介绍的,using the provided identity value and an associative accumulation function。

先看是associative,“结合性”,就是说要满足,

a op b op c = a op (b op c)

像加法,最小值,最大值,字符拼接都有这个性质。

API 还说,reduce函数等价于,

T result = identity;
for (T element : this stream)
result = accumulator.apply(result, element)
return result;

那identy是什么呢?对于操作的集合的所有t,应该有

accumulator.apply(identity, t) = t

是的,它就是离散数学里面提到的1元。对加法而言,1元就是0。

reduce操作总会返回新的值。然而,累积函数在每次处理流中的一个元素时也返回一个新的值。假设你要把流“reduce”成一个集合,你每添加一个元素处理就会产生一个新的结合,这显然是个性能上的缺憾,是很低效的。这时,你可以考虑更新已有的集合。这就是collect方法做的事情。

下一篇再介绍collect的使用细节。

1 0

发表评论