程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 初探Lambda表達式/Java多核編程【0】從內部迭代到外部迭代

初探Lambda表達式/Java多核編程【0】從內部迭代到外部迭代

編輯:關於JAVA

初探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循環遍歷自身,只不過對並發做了一些處置而已。
可見內部迭代與外部迭代並沒有實質上的區別,兩者存在方式上的不同。
外部迭代的更多優勢與特性隨著本書的深化將會逐步顯現。


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