亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Java > 正文

Lambda表達式和Java集合框架

2019-11-06 06:53:37
字體:
來源:轉載
供稿:網友

http://www.cnblogs.com/CarpenterLee/p/6507161.html

本文github地址

java8為容器新增一些有用的方法,這些方法有些是為完善原有功能,有些是為引入函數式編程(Lambda表達式),學習和使用這些方法有助于我們寫出更加簡潔有效的代碼.本文分別以ArrayList和HashMap為例,講解Java8集合框架(Java Collections Framework)中新加入方法的使用.

前言

我們先從最熟悉的Java集合框架(Java Collections Framework, JCF)開始說起。

為引入Lambda表達式,Java8新增了java.util.funcion包,里面包含常用的函數接口,這是Lambda表達式的基礎,Java集合框架也新增部分接口,以便與Lambda表達式對接。

首先回顧一下Java集合框架的接口繼承結構:

JCF_Collection_Interfaces

上圖中綠色標注的接口類,表示在Java8中加入了新的接口方法,當然由于繼承關系,他們相應的子類也都會繼承這些新方法。下表詳細列舉了這些方法。

接口名Java8新加入的方法
CollectionremoveIf() spliterator() stream() parallelStream() forEach()
ListreplaceAll() sort()
MapgetOrDefault() forEach() replaceAll() putIfAbsent() remove() replace() computeIfAbsent() computeIfPResent() compute() merge()

這些新加入的方法大部分要用到java.util.function包下的接口,這意味著這些方法大部分都跟Lambda表達式相關。我們將逐一學習這些方法。

Collection中的新方法

如上所示,接口CollectionList新加入了一些方法,我們以是List的子類ArrayList為例來說明。了解Java7ArrayList實現原理,將有助于理解下文。

forEach()

該方法的簽名為void forEach(Consumer<? super E> action),作用是對容器中的每個元素執行action指定的動作,其中Consumer是個函數接口,里面只有一個待實現方法void accept(T t)(后面我們會看到,這個方法叫什么根本不重要,你甚至不需要記憶它的名字)。

需求:假設有一個字符串列表,需要打印出其中所有長度大于3的字符串.

Java7及以前我們可以用增強的for循環實現:

// 使用曾強for循環迭代ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));for(String str : list){ if(str.length()>3) System.out.println(str);}

現在使用forEach()方法結合匿名內部類,可以這樣實現:

// 使用forEach()結合匿名內部類迭代ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));list.forEach(new Consumer<String>(){ @Override public void accept(String str){ if(str.length()>3) System.out.println(str); }});

上述代碼調用forEach()方法,并使用匿名內部類實現Comsumer接口。到目前為止我們沒看到這種設計有什么好處,但是不要忘記Lambda表達式,使用Lambda表達式實現如下:

// 使用forEach()結合Lambda表達式迭代ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));list.forEach( str -> { if(str.length()>3) System.out.println(str); });

上述代碼給forEach()方法傳入一個Lambda表達式,我們不需要知道accept()方法,也不需要知道Consumer接口,類型推導幫我們做了一切。

removeIf()

該方法簽名為boolean removeIf(Predicate<? super E> filter),作用是刪除容器中所有滿足filter指定條件的元素,其中Predicate是一個函數接口,里面只有一個待實現方法boolean test(T t),同樣的這個方法的名字根本不重要,因為用的時候不需要書寫這個名字。

需求:假設有一個字符串列表,需要刪除其中所有長度大于3的字符串。

我們知道如果需要在迭代過程沖對容器進行刪除操作必須使用迭代器,否則會拋出ConcurrentModificationException,所以上述任務傳統的寫法是:

// 使用迭代器刪除列表元素ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));Iterator<String> it = list.iterator();while(it.hasNext()){ if(it.next().length()>3) // 刪除長度大于3的元素 it.remove();}

現在使用removeIf()方法結合匿名內部類,我們可是這樣實現:

// 使用removeIf()結合匿名名內部類實現ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));list.removeIf(new Predicate<String>(){ // 刪除長度大于3的元素 @Override public boolean test(String str){ return str.length()>3; }});

上述代碼使用removeIf()方法,并使用匿名內部類實現Precicate接口。相信你已經想到用Lambda表達式該怎么寫了:

// 使用removeIf()結合Lambda表達式實現ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));list.removeIf(str -> str.length()>3); // 刪除長度大于3的元素

使用Lambda表達式不需要記憶Predicate接口名,也不需要記憶test()方法名,只需要知道此處需要一個返回布爾類型的Lambda表達式就行了。

replaceAll()

該方法簽名為void replaceAll(UnaryOperator<E> operator),作用是對每個元素執行operator指定的操作,并用操作結果來替換原來的元素。其中UnaryOperator是一個函數接口,里面只有一個待實現函數T apply(T t)。

需求:假設有一個字符串列表,將其中所有長度大于3的元素轉換成大寫,其余元素不變。

Java7及之前似乎沒有優雅的辦法:

// 使用下標實現元素替換ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));for(int i=0; i<list.size(); i++){ String str = list.get(i); if(str.length()>3) list.set(i, str.toUpperCase());}

使用replaceAll()方法結合匿名內部類可以實現如下:

// 使用匿名內部類實現ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));list.replaceAll(new UnaryOperator<String>(){ @Override public String apply(String str){ if(str.length()>3) return str.toUpperCase(); return str; }});

上述代碼調用replaceAll()方法,并使用匿名內部類實現UnaryOperator接口。我們知道可以用更為簡潔的Lambda表達式實現:

// 使用Lambda表達式實現ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));list.replaceAll(str -> { if(str.length()>3) return str.toUpperCase(); return str;});

sort()

該方法定義在List接口中,方法簽名為void sort(Comparator<? super E> c),該方法根據c指定的比較規則對容器元素進行排序。Comparator接口我們并不陌生,其中有一個方法int compare(T o1, T o2)需要實現,顯然該接口是個函數接口。

需求:假設有一個字符串列表,按照字符串長度增序對元素排序。

由于Java7以及之前sort()方法在Collections工具類中,所以代碼要這樣寫:

// Collections.sort()方法ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));Collections.sort(list, new Comparator<String>(){ @Override public int compare(String str1, String str2){ return str1.length()-str2.length(); }});

現在可以直接使用List.sort()方法,結合Lambda表達式,可以這樣寫:

// List.sort()方法結合Lambda表達式ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));list.sort((str1, str2) -> str1.length()-str2.length());

spliterator()

方法簽名為Spliterator<E> spliterator(),該方法返回容器的可拆分迭代器。從名字來看該方法跟iterator()方法有點像,我們知道Iterator是用來迭代容器的,Spliterator也有類似作用,但二者有如下不同:

Spliterator既可以像Iterator那樣逐個迭代,也可以批量迭代。批量迭代可以降低迭代的開銷。Spliterator是可拆分的,一個Spliterator可以通過調用Spliterator<T> trySplit()方法來嘗試分成兩個。一個是this,另一個是新返回的那個,這兩個迭代器代表的元素沒有重疊。

可通過(多次)調用Spliterator.trySplit()方法來分解負載,以便多線程處理。

stream()和parallelStream()

stream()parallelStream()分別返回該容器的Stream視圖表示,不同之處在于parallelStream()返回并行的Stream。Stream是Java函數式編程的核心類,我們會在后面章節中學習。

Map中的新方法

相比Collection,Map中加入了更多的方法,我們以HashMap為例來逐一探秘。了解Java7HashMap實現原理,將有助于理解下文。

forEach()

該方法簽名為void forEach(BiConsumer<? super K,? super V> action),作用是Map中的每個映射執行action指定的操作,其中BiConsumer是一個函數接口,里面有一個待實現方法void accept(T t, U u)。BinConsumer接口名字和accept()方法名字都不重要,請不要記憶他們。

需求:假設有一個數字到對應英文單詞的Map,請輸出Map中的所有映射關系.

Java7以及之前經典的代碼如下:

// Java7以及之前迭代MapHashMap<Integer, String> map = new HashMap<>();map.put(1, "one");map.put(2, "two");map.put(3, "three");for(Map.Entry<Integer, String> entry : map.entrySet()){ System.out.println(entry.getKey() + "=" + entry.getValue());}

使用Map.forEach()方法,結合匿名內部類,代碼如下:

// 使用forEach()結合匿名內部類迭代MapHashMap<Integer, String> map = new HashMap<>();map.put(1, "one");map.put(2, "two");map.put(3, "three");map.forEach(new BiConsumer<Integer, String>(){ @Override public void accept(Integer k, String v){ System.out.println(k + "=" + v); }});

上述代碼調用forEach()方法,并使用匿名內部類實現BiConsumer接口。當然,實際場景中沒人使用匿名內部類寫法,因為有Lambda表達式:

// 使用forEach()結合Lambda表達式迭代MapHashMap<Integer, String> map = new HashMap<>();map.put(1, "one");map.put(2, "two");map.put(3, "three");map.forEach((k, v) -> System.out.println(k + "=" + v));}

getOrDefault()

該方法跟Lambda表達式沒關系,但是很有用。方法簽名為V getOrDefault(Object key, V defaultValue),作用是按照給定的key查詢Map中對應的value,如果沒有找到則返回defaultValue。使用該方法程序員可以省去查詢指定鍵值是否存在的麻煩.

需求;假設有一個數字到對應英文單詞的Map,輸出4對應的英文單詞,如果不存在則輸出NoValue

// 查詢Map中指定的值,不存在時使用默認值HashMap<Integer, String> map = new HashMap<>();map.put(1, "one");map.put(2, "two");map.put(3, "three");// Java7以及之前做法if(map.containsKey(4)){ // 1 System.out.println(map.get(4));}else{ System.out.println("NoValue");}// Java8使用Map.getOrDefault()System.out.println(map.getOrDefault(4, "NoValue")); // 2

putIfAbsent()

該方法跟Lambda表達式沒關系,但是很有用。方法簽名為V putIfAbsent(K key, V value),作用是只有在不存在key值的映射或映射值為null,才將value指定的值放入到Map中,否則不對Map做更改.該方法將條件判斷和賦值合二為一,使用起來更加方便.

remove()

我們都知道Map中有一個remove(Object key)方法,來根據指定key值刪除Map中的映射關系;Java8新增了remove(Object key, Object value)方法,只有在當前Mapkey正好映射到value才刪除該映射,否則什么也不做.

replace()

在Java7及以前,要想替換Map中的映射關系可通過put(K key, V value)方法實現,該方法總是會用新值替換原來的值.為了更精確的控制替換行為,Java8在Map中加入了兩個replace()方法,分別如下:

replace(K key, V value),只有在當前Mapkey的映射存在時才用value去替換原來的值,否則什么也不做.replace(K key, V oldValue, V newValue),只有在當前Mapkey的映射存在且等于oldValue才用newValue去替換原來的值,否則什么也不做.

replaceAll()

該方法簽名為replaceAll(BiFunction<? super K,? super V,? extends V> function),作用是對Map中的每個映射執行function指定的操作,并用function的執行結果替換原來的value,其中BiFunction是一個函數接口,里面有一個待實現方法R apply(T t, U u).不要被如此多的函數接口嚇到,因為使用的時候根本不需要知道他們的名字.

需求:假設有一個數字到對應英文單詞的Map,請將原來映射關系中的單詞都轉換成大寫.

Java7以及之前經典的代碼如下:

// Java7以及之前替換所有Map中所有映射關系HashMap<Integer, String> map = new HashMap<>();map.put(1, "one");map.put(2, "two");map.put(3, "three");for(Map.Entry<Integer, String> entry : map.entrySet()){ entry.setValue(entry.getValue().toUpperCase());}

使用replaceAll()方法結合匿名內部類,實現如下:

// 使用replaceAll()結合匿名內部類實現HashMap<Integer, String> map = new HashMap<>();map.put(1, "one");map.put(2, "two");map.put(3, "three");map.replaceAll(new BiFunction<Integer, String, String>(){ @Override public String apply(Integer k, String v){ return v.toUpperCase(); }});

上述代碼調用replaceAll()方法,并使用匿名內部類實現BiFunction接口。更進一步的,使用Lambda表達式實現如下:

// 使用replaceAll()結合Lambda表達式實現HashMap<Integer, String> map = new HashMap<>();map.put(1, "one");map.put(2, "two");map.put(3, "three");map.replaceAll((k, v) -> v.toUpperCase());

簡潔到讓人難以置信.

merge()

該方法簽名為merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction),作用是:

如果Mapkey對應的映射不存在或者為null,則將value(不能是null)關聯到key上;否則執行remappingFunction,如果執行結果非null則用該結果跟key關聯,否則在Map中刪除key的映射.

參數中BiFunction函數接口前面已經介紹過,里面有一個待實現方法R apply(T t, U u)

merge()方法雖然語義有些復雜,但該方法的用方式很明確,一個比較常見的場景是將新的錯誤信息拼接到原來的信息上,比如:

map.merge(key, newMsg, (v1, v2) -> v1+v2);

compute()

該方法簽名為compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction),作用是把remappingFunction的計算結果關聯到key上,如果計算結果為null,則在Map中刪除key的映射.

要實現上述merge()方法中錯誤信息拼接的例子,使用compute()代碼如下:

map.compute(key, (k,v) -> v==null ? newMsg : v.concat(newMsg));

computeIfAbsent()

該方法簽名為V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction),作用是:只有在當前Map不存在key值的映射或映射值為null,才調用mappingFunction,并在mappingFunction執行結果非null時,將結果跟key關聯.

Function是一個函數接口,里面有一個待實現方法R apply(T t)

computeIfAbsent()常用來對Map的某個key值建立初始化映射.比如我們要實現一個多值映射,Map的定義可能是Map<K,Set<V>>,要向Map中放入新值,可通過如下代碼實現:

Map<Integer, Set<String>> map = new HashMap<>();// Java7及以前的實現方式if(map.containsKey(1)){ map.get(1).add("one");}else{ Set<String> valueSet = new HashSet<String>(); valueSet.add("one"); map.put(1, valueSet);}// Java8的實現方式map.computeIfAbsent(1, v -> new HashSet<String>()).add("yi");

使用computeIfAbsent()將條件判斷和添加操作合二為一,使代碼更加簡潔.

computeIfPresent()

該方法簽名為V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction),作用跟computeIfAbsent()相反,即,只有在當前Map存在key值的映射且非null,才調用remappingFunction,如果remappingFunction執行結果為null,則刪除key的映射,否則使用該結果替換key原來的映射.

這個函數的功能跟如下代碼是等效的:

// Java7及以前跟computeIfPresent()等效的代碼if (map.get(key) != null) { V oldValue = map.get(key); V newValue = remappingFunction.apply(key, oldValue); if (newValue != null) map.put(key, newValue); else map.remove(key); return newValue;}return null;

總結

Java8為容器新增一些有用的方法,這些方法有些是為完善原有功能,有些是為引入函數式編程,學習和使用這些方法有助于我們寫出更加簡潔有效的代碼.函數接口雖然很多,但絕大多數時候我們根本不需要知道它們的名字,書寫Lambda表達式時類型推斷幫我們做了一切.

本文github地址


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
国产免费观看久久黄| 中文字幕av一区二区| 91精品国产自产在线老师啪| 欧美多人爱爱视频网站| 亚洲精品999| 久久九九全国免费精品观看| 亚洲欧美日韩综合| 91久久久在线| 欧美日韩激情小视频| 久久亚洲欧美日韩精品专区| 91精品在线观看视频| 日韩有码视频在线| 日本在线观看天堂男亚洲| 欧美乱大交xxxxx另类电影| 2020欧美日韩在线视频| 孩xxxx性bbbb欧美| 2019中文在线观看| 国产成人亚洲综合| 黄色成人av在线| 在线播放精品一区二区三区| 国产精品视频白浆免费视频| 色99之美女主播在线视频| 日韩欧美国产中文字幕| 国产成人一区二区三区| 亚洲欧美日韩视频一区| 最近2019中文字幕在线高清| 美女撒尿一区二区三区| 疯狂欧美牲乱大交777| 欧美电影免费观看电视剧大全| 欧美日韩一区二区在线播放| 91免费国产网站| 欧美洲成人男女午夜视频| 国产精品福利在线观看网址| 亚洲影院色无极综合| 欧美激情第一页xxx| 日本午夜在线亚洲.国产| 亚洲国产欧美一区二区三区同亚洲| 这里精品视频免费| 国产精品无码专区在线观看| 日韩精品免费观看| 久热爱精品视频线路一| 久久成人精品一区二区三区| 日韩av影院在线观看| 在线精品国产成人综合| 国产精品视频一区二区高潮| 久久久视频免费观看| 国精产品一区一区三区有限在线| 青青草成人在线| 欧美日韩精品在线视频| 在线观看日韩欧美| 久久av红桃一区二区小说| 夜夜嗨av一区二区三区四区| 亚洲欧美国产精品专区久久| 欧美一级高清免费播放| 91久久在线视频| 91国语精品自产拍在线观看性色| zzijzzij亚洲日本成熟少妇| 亚洲第一福利网站| 国产精品第10页| 日韩av电影免费观看高清| 国产精品露脸自拍| 丝袜美腿精品国产二区| 成人免费视频网| 亚洲影视中文字幕| 亚洲午夜精品视频| 精品久久久久久亚洲国产300| 欧美精品九九久久| 亚洲精品国产电影| 久久综合伊人77777蜜臀| 久国内精品在线| 国产日韩欧美综合| 国产视频在线一区二区| 久久99久国产精品黄毛片入口| 久久久国产精彩视频美女艺术照福利| 日本精品久久电影| 国产亚洲欧美日韩美女| 久久天天躁狠狠躁夜夜躁| 午夜精品蜜臀一区二区三区免费| 久久久国产影院| 欧美黄色片在线观看| 欧美激情第三页| 久久777国产线看观看精品| 亚洲**2019国产| 欧美亚洲在线播放| 91久久久亚洲精品| 欧美激情一区二区三级高清视频| 国产精品久久久一区| 亚洲a成v人在线观看| 91爱视频在线| 日韩国产欧美区| 国产精品一区二区三区久久| 久久精视频免费在线久久完整在线看| 久久国产精品电影| 中文字幕亚洲欧美在线| 亚洲视屏在线播放| 91成人精品网站| 精品久久久久人成| 91香蕉嫩草神马影院在线观看| 成人写真福利网| 一区二区三区黄色| 欧美性猛交丰臀xxxxx网站| 亚洲精品一区二区在线| 欧美激情视频一区二区| 国产97色在线| 欧美日韩一区二区精品| 成人国产精品一区二区| 欧美最猛性xxxxx(亚洲精品)| 精品久久久久久中文字幕大豆网| 91九色单男在线观看| 国产中文欧美精品| 日本人成精品视频在线| 中文字幕欧美视频在线| 国产精品视频一区二区高潮| 亚洲国产一区二区三区在线观看| 欧美精品久久久久久久| 亚洲欧美成人精品| 欧美亚洲视频在线观看| 欧美日韩国产精品一区二区三区四区| www.色综合| 曰本色欧美视频在线| 日韩成人激情影院| 欧美猛少妇色xxxxx| 一区二区成人精品| 久久国产精品偷| 国产欧美精品在线播放| 成人久久一区二区| 亚洲色图五月天| 国产成人鲁鲁免费视频a| 欧美日韩人人澡狠狠躁视频| 欧美性猛交99久久久久99按摩| 伊人久久久久久久久久| 国产午夜精品一区二区三区| 国内免费久久久久久久久久久| 国外色69视频在线观看| 91精品国产91久久| 国产一区二区三区四区福利| 97精品在线观看| 亚洲国产成人精品久久| 91国偷自产一区二区三区的观看方式| 69久久夜色精品国产7777| 国产精品9999| xvideos亚洲| 欧美裸体男粗大视频在线观看| 亚洲天堂av在线播放| xvideos成人免费中文版| 日韩大陆毛片av| 国产精品免费网站| 91精品视频一区| 欧美激情亚洲精品| 欧美日韩综合视频| 亚洲色图偷窥自拍| 性色av一区二区三区免费| 成人激情在线观看| 欧美大片va欧美在线播放| 成人黄色大片在线免费观看| 91精品在线观| 粉嫩av一区二区三区免费野| 久久国产精品网站| 91久久久久久久久久久| 中文字幕视频一区二区在线有码| 久久成人免费视频| 欧美美女15p| 亚洲va电影大全| 日本精品久久久久久久|