https://my.oschina.net/wadelau/blog/1859419
Java forEach中 Lambda Expr中的 final變量要求
Java8閉包
閉包是一個函數在創建時允許該自身函數訪問並操作該自身函數之外的變量時所創建的作用域。換句話說,閉包可以讓函數訪問所有的變量和函數,只要這些變量和函數存在於該函數聲明時的作用域內就行。
本文是關於 -Java Lambda Expression在forEach方法的應用討論。對比其他編程語言的foreach 操作(文末附帶7種主要編程語言的Loop HashMap by forEach的程序片段),Java 8引入的運用 Lambda Expression方式的 forEach操作方法是最接近語言所要表達的本意,且簡潔、直接。
在持續優化 -GWA2 in -Java 過程中,由於 -GWA2 多層結構設計,層間數據傳遞很多依賴Map/HashMap完成,經常用到這個 forEach 並碰到一些問題(引入外部變量,有條件篩選及終止等),茲記錄相關探索過程如下。
通常在 -Java 中遍歷一個數據集合是常見的操作場景,比如遍歷數組 (-R/G2ST ):
int[] numbers = {1,2,3,4,5,6,7,8,9,10}; for (int item : numbers) { System.out.println("Count is: " + item); }
再比如常規的借助 keySet遍歷一個key-value結構的 Map / Hash, 哈希 (-R/k2SP ):
for (KeyType key : m.keySet()){ System.out.println(key+", "+m.get(key)); }
-R/k2SP 列舉了三種方法進行遍歷key-value 的Map, 實際上根據 -R/92SS 頁面上第二個回答所進行的測試,大概有十多種方法可以實現遍歷一個 key-value 的Map. 這么多方法中,又以 forEach 的表達最為直接、高效,所以推薦使用 Map.forEach .
forEach 本質上是建造一個 Lambda Expression 並進行運算,其中 Map.forEach 等同的表達式也是針對 keySet的調用( -R/V2SQ )。
//- Map.forEach is equivalent to: for (Map.Entry<K, V> entry : map.entrySet()) action.accept(entry.getKey(), entry.getValue());//- an example
HashMap<String, Integer> hm = new HashMap<String, Integer>();
hm.forEach((key, val)->{
System.out.println(key+”, “+val);
});
由於 Lambda Expression 是一個 enclosing scope,它與代碼區塊外的溝通成為問題,外部變量無法在 Lambda 內部調用,內部變量也無法在外部訪問到。在這近乎隔離的運行時環境里,還留了final變量可以穿行的縫隙,如果要實現將Lambda Expr與外部變量進行數據交換,就需要在代碼區塊外部定義final類型變量作為數據信息載體。常見的應用有如下兩個場景.
- 將Map/HashMap的數據遍歷並加工
例子程序:HashMap<String, Integer> hm = new HashMap<String, Integer>();
final HashMap<String, Integer> hm2 = new HashMap<String, Integer>();
hm.forEach((key, val)->{
System.out.println(key+”, “+val);
hm2.put(key, val + 1);
});借助 Lambda Expression 對 final變量的支持,可以容易地將遍歷並加工后的數據中轉取出做進一步的操作.
final 變量由於無法再次出現在賦值語句的左邊,也即無法對對象做再次賦值操作。但這不影響我們后續將 hm2的值進行遍歷或者將其復制到另外一個非final的對象中去,如上例中的 hm.
同樣地,作為final的 hm2 無法整體被賦值,但可以對其本身進行操作,如上例中的,通過 hm2.put 將某個元素放入這個容器中。類似的還有,比如 StringBuffer 的操作。StringBuffer pageIdsb = new StringBuffer(“0”);
pageList.forEach((k, v)->{
HashMap tmphm = (HashMap)v;
pageIdsb.append(“,”).append(tmphm.get(“id”));
});public final class StringBuffer extends Object implements Serializable, CharSequence
由 StringBuffer 類的定義(-R/V2SQ ),我們知道其是 final 類,於是在初始化后,可以在 Lambda Expr中通過 pageIdsb.append 的方式進行其值的操作與變化。
- 有條件過濾或推出、終止遍歷
在遍歷Map/HashMap等對象集合時,有時候需要有條件的過濾或者終止,如果將外部約束條件帶入?
又如何定義內部自循環變量?
一種可行的方式,依然是使用 final變量,然后基本變量類型,如果定義為final之后則不能被直接修改,無法滿足循環體內計數的需求.
於是就需要設計一個復合final類的變量,其成員一個負責約束條件,另外一個負責循環計數。HashMap<String, Integer> hm = new HashMap<String, Integer>();
final HashMap<String, Integer> hm2 = new HashMap<String, Integer>(){{
put(“iCount”, 0);
put(“maxCount”, 4);
}};
hm.forEach((key, val)->{
int iCount = Wht.parseInt(hm2.get(“iCount”));
int maxCount = Wht.parseInt(hm2.get(“maxCount”));
if(iCount < maxCount){
System.out.println(key+”, “+val);
}
hm2.put(“iCount”, iCount + 1);
});在上面的例子程序中,我們定義了循環體內計數變量 iCount 和 滿足退出條件 maxCount 每次循環時取出並做對比,每次循環時, iCount++ 。
上面例子程序中的 Wht.parseInt 是 -GWA2 的基礎設施類,可以在 -GWA2@Github (-R/h2SO )下載到.
上面例子程序中的hm2 的聲明實例中,通過構造方法並以一個匿名類完成了兩個 put操作。關於匿名類 Anonymous Class,參考 -Java 文檔(-R/i2SP ), 在 -GWA2 in -Java 里也有不少應用。
-Java 中引入的 forEach 操作,盡管是以 Lambda Expression為載體的一個 Consumer / BiConsumer對象,運行在一個 enclosing scope,依靠可以外部 final 變量這個渠道,可以提供媲美其他編程語言一樣的 foreach 效果,對增加程序可讀性、可維護性有一定的幫助,已有的評測 forEach也表現出不錯的性能,值得更多地應用。
-GWA2 是”通用網絡應用架構( General Web Application Architeture )”,基於 -GWA2 可以輕便構建各種網絡應用程序,
包括復雜的在線購物商城、 旅游交易平台、社群或者社交網站和新聞資訊網站等,
也包括各種企事業單位網上門戶,在線交互及服務作業系統等.
還可以包括為NativeApp做服務器端支持, 甚至是WebApp的全部.
-GWA2 是為數不多的支持跨開發語言的應用框架,目前支持 -Java, -PHP, -Perl, -Aspx and -Python .
-GWA2 is a “General Web Application Architecture” and based on -GWA2 developers can easily build a variety of network applications,
including complex online shopping malls, travel trading platforms, community or social networking sites and news information sites, etc.
Also the applications include various online portals of enterprises and institutions, online interaction and service operations systems.
Moreover it contains server-side support for NativeApp, or even all of the WebApp.
-GWA2 is one of the web frameworks which provide cross-language support for -Java, -PHP, -Perl, -Aspx and -Python at present.
-GWA2 is E.A.S.Y
Easy Along, Swift Yield
輕松啟動, 快速產出.
----
(1) Loop HashMap in PHP ****
$hm = array(“a”=>1, “b”=>2, “c”=>3);
foreach($hm as $key=>$val){
print “key:$key , val:$val\n”;
}
(2) Loop HashMap in Perl ***
my %hm = (“a”=>1, “b”=>2, “c”=>3);
foreach my $key (keys %hm){
my $val = $hm{$key};
print “key:$key , val:$val\n”;
}
(3) Loop HashMap in Swift *****
let hm = [“a”:1, “b”:2, “c”:3];
for (key, val) in hm {
print(“key:\(key) , val:\(val)\n”);
}
(4) Loop HashMap in Python ***
hm = {“a”:1, “b”:2, “c”:3};
for key in hm:
print(“key:{} , val:{}”.format(key, hm[key]));
(5) Loop HashMap in C++ *
std::map<std::string, std::int> hm {
{“a”, 1},
{“b”, 1},
{“c”, 3}
};
for(const auto& key : hm){
std::cout << “key:” << key.first
<< ” , val:” << key.second
<< “\n”;
}
(6) Loop HashMap in JavaScript ***
var hm = {“a”:1, “b”:2, “c”:3};
for (var key in hm){
console.log(“key:”+key+” , val:”+hm[key]);
};
(7) Loop HashMap in Java ***
HashMap<String, Integer> hm = new HashMap<String, Integer>(){{
put(“a”, 1″);
put(“b”, 2);
put(“c”, 3);
}};
hm.forEach((key, val)->{
System.out.println(key+”, “+val);
});
----