首页 > 代码库 > Java:聚集操作
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,其操作时很多的,部分如下,
具体用法不一一介绍。
我们再看一个需求,求“重量大于20的背包的平均重量”。
double average = packages .stream() .filter(e -> e.getWeight() > 20) .mapToInt(e -> e.getWeigh()) .average() .getAsDouble();
除了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的使用细节。
Java:聚集操作