初探Lambda表達式/Java多核編程【0】從內部迭代到外部迭代。本站提示廣大學習愛好者:(初探Lambda表達式/Java多核編程【0】從內部迭代到外部迭代)文章只能為提供參考,不一定能成為您想要的結果。以下是初探Lambda表達式/Java多核編程【0】從內部迭代到外部迭代正文
放假前從學校圖書館中借來一本書,Oracle官方的《通曉Lambda表達式:Java多核編程》。
假期已過大半才想起來還沒翻上幾頁,在此先引薦給大家。
此書內容簡約干練,假如你對Java語法有根底的看法看起來就會不費力,獨一的缺陷就是代碼局部的內容以及排版有些錯誤,不過瑕不掩瑜,無傷大雅。
這個系列就是我對書中每一大節的一個提煉、總結以及理論,每一個知識點我都會附上自己寫的代碼,這些代碼用來驗證所學的知識。
才疏學淺,假如有了解錯誤之處請指正,歡送交流討論。
最傳統的辦法大約是用Iterator,當然我比擬Low,習氣用i<arr.size()
這類的循環。(如今我用for/in,實質上還是Iterator...)
這一類辦法叫做內部迭代,意為顯式地停止迭代操作,即集合中的元素訪問是由一個處於集合內部的東西來控制的,在這裡控制著循環的東西就是迭代器。
書中舉的例子是pointList,我在這裡把它換成一個電話簿。
public class ContactList extends ArrayList<String>{}
外面存儲著String類型的聯絡人。
for (Iterator<String> contactListIterator = contactList.iterator(); contactListIterator.hasNext(); ) {
System.out.println(contactListIterator.next());
}
如今我們將這種遍歷方式換成外部迭代。
望文生義,這種方式的遍歷將在集合外部停止,我們不會顯式地去控制這個循環。
無需關懷遍歷元素的順序,我們只需求定義對其中每一個元素停止什麼樣的操作。留意在這種設定下能夠無法直接獲取到以後元素的下標。
Java5中引入Collection.forEach(...)
,對集合中每一個元素使用其行為參數,這個辦法是從其父接口Iterable中承繼。
如今我們可以去重寫forEach辦法。
@Override
public void forEach() {
for(String s : this) {
System.out.println(s + " is your contact.");
}
}
這下我們對電話簿調用forEach辦法的時分,遍歷操作就會在類的外部完成了。
當然這看起來十分傻並且很不靈敏。假如我們想把行為作為參數傳給forEach呢?
所幸Java8提供了這種能夠,這種行為參數叫做Consumer。
public interface Consumer<T> {
void accept(T t);
}
一個分明的函數式接口,行為定義在accept辦法中。
在定義好了我們自己的Consumer之後,如今這樣寫:
@Override
public void forEach(Consumer<String> c) {
for(String s : this) {
c.accept(s);
}
}
傻的水平加重了一些,當然還是不夠機智。
在這種狀況下一個匿名外部類就能搞定問題(Android外面監聽器寫到手抽...)
contactList.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s + " is your contact.");
}
});
這下足夠簡約,當然聰明的編譯器應該可以推斷出我們傳入forEach辦法的只能是一個Consumer並且需求調用的就是Consumer的獨一辦法accept,這段代碼還有簡化的余地。
所以如今需求退場的就是Lambda表達式了。
既然我們的電話簿ContactList實質上是一個ArrayList<String>
,那麼編譯器也一定能推斷出Consumer的類型參數標識為String。
所以這下連參數類型都省了。
contactList.forEach(s -> System.out.println(s + " is your contact, again!"));
->前的s為參數名(即String s
中的s),->後為辦法體,也可以用一個大括號括起來,由於我這裡只寫了一句所以就沒用。
這就是外部迭代和Lambda相結合的終極奧義了。
如今附上測試代碼:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.function.Consumer;
public class ContactList extends ArrayList<String> {
@Override
public void forEach(Consumer<? super String> action) {
super.forEach(action);
}
public static void main(String[] args) {
ContactList contactList = new ContactList();
contactList.add("Foo");
contactList.add("Bar");
contactList.add("Nico");
for (Iterator<String> contactListIterator = contactList.iterator(); contactListIterator.hasNext(); ) {
System.out.println(contactListIterator.next());
}
System.out.println("\n--- Consumer is coming! ---\n");
contactList.forEach(new ContactAction());
System.out.println("\n--- Lambda is coming! ---\n");
contactList.forEach(s -> System.out.println(s + " is your contact, again!"));
}
static class ContactAction implements Consumer<String> {
@Override
public void accept(String s) {
System.out.println(s + " is your contact.");
}
}
}
以及運轉後果:
Foo Bar Nico --- Consumer is coming! --- Foo is your contact. Bar is your contact. Nico is your contact. --- Lambda is coming! --- Foo is your contact, again! Bar is your contact, again! Nico is your contact, again!
此外我們再進入forEach辦法一看終究:
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int expectedModCount = modCount;
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) this.elementData;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
action.accept(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
可以發現其外部照舊是運用了一個for循環遍歷自身,只不過對並發做了一些處置而已。
可見內部迭代與外部迭代並沒有實質上的區別,兩者存在方式上的不同。
外部迭代的更多優勢與特性隨著本書的深化將會逐步顯現。