副标题#e#
Oracle在2014年3月19日如期宣布了Java 8。Java 8版本被认为是具有里程碑意义的一个版本,Oracle在该版本中添加了很多新特性,包罗Lambda表达式、要领引用、增强了安详等等。
在浩瀚的新特性中,聚合操纵(Aggregate Operations)是针对荟萃类的一个较量大的变革。通过聚合操纵,开拓者可以更容易地利用Lambda表达式,而且更利便地实现对荟萃的查找、遍历、过滤以及常见计较等。
聚合操纵与Java 8中的Lambda表达式、要领引用等新特性是相关的,一般一起组合利用,但这里只说明聚合操纵的利用,下面就聚合操纵的利用举办简朴说明。
荟萃类的条理布局
荟萃类是Java语言提供的帮助类,是一种较为通用的数据布局,如Map、Set、List等。Java中荟萃类条理干系如下:
图 1
如上图,Collection是主要荟萃类的接口,其子接口(具化接口)有Deque、Queue、Set、List等。
Map是另一种范例的荟萃,以Key、Value的键值对存储数据集。
在Java 8中,在java.util.Collection接口中添加了如下要领:
Stream<E> stream() { return StreamSupport.stream(spliterator(), false); }
stream()要领的可见性修饰符为default,这又是Java 8的新特性。在接口中(Collection为interface),本不需要(也不能)举办要领实现,但引入default修饰后就差异了。开拓者不单可以举办要领的实现,并且还不消思量向后兼容的问题。关于Default Method的具体表明,读者可以参考Java 8的官方文档。
正是stream要领引出了荟萃类的聚合操纵。
[留意]Map接口中并没有stream()要领,可是Map的values()和keySet()均返回荟萃工具,在荟萃工具上虽然是可以利用stream()要领的。
#p#副标题#e#
聚合操纵实例
为说明聚合操纵的利用,首先界说一个数据元素类Person,如下:
import java.time.LocalDate; public class Person { String name; LocalDate birthday; Sex gender; String emailAddress; public int getAge() { return LocalDate.now().getYear() - birthday.getYear(); } public void setBirthday(LocalDate birthday){ this.birthday = birthday; } public void setGender(Sex sex){ this.gender = sex; } public void printPerson() { System.out.println("The name is " + name); } public Sex getGender(){ return gender; } public enum Sex { MALE, FEMALE } }
在Java 8以前的版本中,对Person荟萃的遍历往往回收以下方法:
Set<Person> persons = new HashSet<Person>();//传统遍历方法 for (Person person : persons) { if (person.getAge() > 18) { System.out.println(person.name + " is elder than 18."); } }
同样的成果,在Java 8中利用聚合操纵,可以实现如下:
//利用聚合操纵 persons.stream().filter(new Predicate<Person>() { @Override public boolean test(Person person) { if (person.getAge() > 18) { return true; } else { return false; } } }).forEach(new Consumer<Person>() { @Override public void accept(Person person) { System.out.println(person.name + " is elder than 18."); } });
首先,在荟萃工具persons上挪用stream()要领(聚合操纵),取得person工具的数据集(elements),然后挪用聚合操纵filter()对荟萃中的元素举办过滤,再挪用forEach()完成对切合条件的person的打印。
Predicate和Consumer为Java 8中界说的函数接口(Functional Interface),在java.util.function包下面,函数接口也是Java 8的新特性。在上述代码中,利用了两个匿名种别离对Predicate和Consumer举办了实现,这两个接口都只有一个要领,这也是函数接口的特征之一。
上述代码中的写法照旧较量繁琐的,为进一步简化,可以利用Lambda表达式实现,如下:
// 利用聚合操纵及Lambda persons.stream() .filter(p -> p.getAge() >= 18) .forEach(p -> System.out.println(p.name + " is elder than 18."));
因为filter()、forEach()的参数均为函数接口,所以可以替换为Lambda表达式的方法。简朴来领略,Lambda表达式就是答允开拓者将代码逻辑作为参数举办通报,关于Lambda表达式的具体内容,请参Java 8的官方文档。
聚合操纵的利用
聚合操纵是Java 8针对荟萃类,使编程更为便利的方法,可以与Lambda表达式一起利用,到达越发简捷的目标。
前面例子中,对聚合操纵的利用可以归结为3个部门:
数据源部门:通过stream()要领,取得荟萃工具的数据集。
通过一系列中间(Intermediate)要领,对数据集举办过滤、检索等数据集的再次处理惩罚。如上例中,利用filter()要领来对数据集举办过滤。
通过最终(terminal)要领完成对数据会合元素的处理惩罚。如上例中,利用forEach()完成对过滤后元素的打印。
中间要领除了filter()外,尚有distinct()、sorted()、map()等等,其一般是对数据集的整理(过滤、排序、匹配、抽取等等),返回值一般也是数据集。
#p#分页标题#e#
最终要领往往是完成对数据会合数据的处理惩罚,如forEach(),尚有allMatch()、anyMatch()、findAny()、findFirst(),数值计较类的要领有sum、max、min、average等等。最终要领也可以是对荟萃的处理惩罚,如reduce()、collect()等等。reduce()要领的处理惩罚方法一般是每次都发生新的数据集,而collect()要领是在原数据集的基本长举办更新,进程中不发生新的数据集。
从上面的例子中可以看出,通过stream()要领,从荟萃工具获取的数据集与荟萃工具的迭代器(Iterator)有些雷同,但他们也不完全沟通:
迭代器提供next()、hasNext()等要领,开拓者可以自行节制对元素的处理惩罚,以及处理惩罚方法,可是只能顺序处理惩罚;
stream()要领返回的数据集无next()等要领,开拓者无法节制对元素的迭代,迭代方法是系统内部实现的,同时系统内的迭代也不必然是顺序的,还可以并行,如parallelStream()要领。并行的方法在一些环境下,可以大幅晋升处理惩罚的效率。
除上述先容的聚合操纵外,Java 8中还提供了其他更为富厚的聚合操纵,读者可以参考Java 8的开拓参考,相识更多内容。
总结
Java 8提供的聚合操纵,以及一起利用的Lambda表达式为开拓者带来了便利,尤其在面向逻辑易变、开拓迭代较快的项目应用时。但笔者小我私家认为,在带来利便的同时,大概也带来了一些贫苦,如沟通逻辑的复用,以及代码的查错、修改等,虽然这些问题也是相对而言的。究竟,任何事物都有两面性,技能在不绝的成长,Java也在不绝地调解本身的适应性,变得成果越来越多,越来越强大了。