程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 淺析Java 8的聚合操作

淺析Java 8的聚合操作

編輯:關於JAVA

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()方法的。

聚合操作實例

為說明聚合操作的使用,首先定義一個數據元素類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()等等,其一般是對數據集的整理(過濾、排序、匹配、抽取等等),返回值一般也是數據集。

最終方法往往是完成對數據集中數據的處理,如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也在不斷地調整自己的適應性,變得功能越來越多,越來越強大了。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved