您好,登錄后才能下訂單哦!
深入淺析Java集合框架與Lambda表達(dá)式?很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來(lái)學(xué)習(xí)下,希望你能有所收獲。
為引入Lambda表達(dá)式,Java8新增了java.util.funcion包
,里面包含常用的函數(shù)接口,這是Lambda表達(dá)式的基礎(chǔ),Java集合框架也新增部分接口,以便與Lambda表達(dá)式對(duì)接。
首先回顧一下Java集合框架的接口繼承結(jié)構(gòu):
上圖中綠色標(biāo)注的接口類,表示在Java8中加入了新的接口方法,當(dāng)然由于繼承關(guān)系,他們相應(yīng)的子類也都會(huì)繼承這些新方法。下表詳細(xì)列舉了這些方法。
接口名 | Java8新加入的方法 |
---|---|
Collection | removeIf() spliterator() stream() parallelStream() forEach() |
List | replaceAll() sort() |
Map | getOrDefault() forEach() replaceAll() putIfAbsent() remove() replace() computeIfAbsent() computeIfPresent() compute() merge() |
這些新加入的方法大部分要用到j(luò)ava.util.function包下的接口,這意味著這些方法大部分都跟Lambda表達(dá)式相關(guān)。我們將逐一學(xué)習(xí)這些方法。
Collection中的新方法
如上所示,接口Collection和List新加入了一些方法,我們以是List的子類ArrayList為例來(lái)說(shuō)明。了解Java7ArrayList實(shí)現(xiàn)原理,將有助于理解下文。
forEach()
該方法的簽名為void forEach(Consumer<? super E> action),作用是對(duì)容器中的每個(gè)元素執(zhí)行action指定的動(dòng)作,其中Consumer是個(gè)函數(shù)接口,里面只有一個(gè)待實(shí)現(xiàn)方法void accept(T t)(后面我們會(huì)看到,這個(gè)方法叫什么根本不重要,你甚至不需要記憶它的名字)。
需求:假設(shè)有一個(gè)字符串列表,需要打印出其中所有長(zhǎng)度大于3的字符串.
Java7及以前我們可以用增強(qiáng)的for循環(huán)實(shí)現(xiàn):
// 使用曾強(qiáng)for循環(huán)迭代 ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too")); for(String str : list){ if(str.length()>3) System.out.println(str); }
現(xiàn)在使用forEach()方法結(jié)合匿名內(nèi)部類,可以這樣實(shí)現(xiàn):
// 使用forEach()結(jié)合匿名內(nèi)部類迭代 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); } });
上述代碼調(diào)用forEach()方法,并使用匿名內(nèi)部類實(shí)現(xiàn)Comsumer接口。到目前為止我們沒(méi)看到這種設(shè)計(jì)有什么好處,但是不要忘記Lambda表達(dá)式,使用Lambda表達(dá)式實(shí)現(xiàn)如下:
// 使用forEach()結(jié)合Lambda表達(dá)式迭代 ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too")); list.forEach( str -> { if(str.length()>3) System.out.println(str); });
上述代碼給forEach()方法傳入一個(gè)Lambda表達(dá)式,我們不需要知道accept()方法,也不需要知道Consumer接口,類型推導(dǎo)幫我們做了一切。
removeIf()
該方法簽名為boolean removeIf(Predicate<? super E> filter),作用是刪除容器中所有滿足filter指定條件的元素,其中Predicate是一個(gè)函數(shù)接口,里面只有一個(gè)待實(shí)現(xiàn)方法boolean test(T t),同樣的這個(gè)方法的名字根本不重要,因?yàn)橛玫臅r(shí)候不需要書(shū)寫這個(gè)名字。
需求:假設(shè)有一個(gè)字符串列表,需要?jiǎng)h除其中所有長(zhǎng)度大于3的字符串。
我們知道如果需要在迭代過(guò)程沖對(duì)容器進(jìn)行刪除操作必須使用迭代器,否則會(huì)拋出ConcurrentModificationException,所以上述任務(wù)傳統(tǒng)的寫法是:
// 使用迭代器刪除列表元素 ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too")); Iterator<String> it = list.iterator(); while(it.hasNext()){ if(it.next().length()>3) // 刪除長(zhǎng)度大于3的元素 it.remove(); }
現(xiàn)在使用removeIf()方法結(jié)合匿名內(nèi)部類,我們可是這樣實(shí)現(xiàn):
// 使用removeIf()結(jié)合匿名名內(nèi)部類實(shí)現(xiàn) ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too")); list.removeIf(new Predicate<String>(){ // 刪除長(zhǎng)度大于3的元素 @Override public boolean test(String str){ return str.length()>3; } });
上述代碼使用removeIf()方法,并使用匿名內(nèi)部類實(shí)現(xiàn)Precicate接口。相信你已經(jīng)想到用Lambda表達(dá)式該怎么寫了:
// 使用removeIf()結(jié)合Lambda表達(dá)式實(shí)現(xiàn) ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too")); list.removeIf(str -> str.length()>3); // 刪除長(zhǎng)度大于3的元素
使用Lambda表達(dá)式不需要記憶Predicate接口名,也不需要記憶test()方法名,只需要知道此處需要一個(gè)返回布爾類型的Lambda表達(dá)式就行了。
replaceAll()
該方法簽名為void replaceAll(UnaryOperator<E> operator),作用是對(duì)每個(gè)元素執(zhí)行operator指定的操作,并用操作結(jié)果來(lái)替換原來(lái)的元素。其中UnaryOperator是一個(gè)函數(shù)接口,里面只有一個(gè)待實(shí)現(xiàn)函數(shù)T apply(T t)。
需求:假設(shè)有一個(gè)字符串列表,將其中所有長(zhǎng)度大于3的元素轉(zhuǎn)換成大寫,其余元素不變。
Java7及之前似乎沒(méi)有優(yōu)雅的辦法:
// 使用下標(biāo)實(shí)現(xiàn)元素替換 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()方法結(jié)合匿名內(nèi)部類可以實(shí)現(xiàn)如下:
// 使用匿名內(nèi)部類實(shí)現(xiàn) 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; } });
上述代碼調(diào)用replaceAll()方法,并使用匿名內(nèi)部類實(shí)現(xiàn)UnaryOperator接口。我們知道可以用更為簡(jiǎn)潔的Lambda表達(dá)式實(shí)現(xiàn):
// 使用Lambda表達(dá)式實(shí)現(xiàn) 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),該方法根據(jù)c指定的比較規(guī)則對(duì)容器元素進(jìn)行排序。Comparator接口我們并不陌生,其中有一個(gè)方法int compare(T o1, T o2)需要實(shí)現(xiàn),顯然該接口是個(gè)函數(shù)接口。
需求:假設(shè)有一個(gè)字符串列表,按照字符串長(zhǎng)度增序?qū)υ嘏判颉?/strong>
由于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(); } });
現(xiàn)在可以直接使用List.sort()方法,結(jié)合Lambda表達(dá)式,可以這樣寫:
// List.sort()方法結(jié)合Lambda表達(dá)式 ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too")); list.sort((str1, str2) -> str1.length()-str2.length());
spliterator()
方法簽名為Spliterator<E> spliterator(),該方法返回容器的可拆分迭代器。從名字來(lái)看該方法跟iterator()方法有點(diǎn)像,我們知道Iterator是用來(lái)迭代容器的,Spliterator也有類似作用,但二者有如下不同:
可通過(guò)(多次)調(diào)用Spliterator.trySplit()方法來(lái)分解負(fù)載,以便多線程處理。
stream()和parallelStream()
stream()和parallelStream()分別返回該容器的Stream視圖表示,不同之處在于parallelStream()返回并行的Stream。Stream是Java函數(shù)式編程的核心類,我們會(huì)在后面章節(jié)中學(xué)習(xí)。
Map中的新方法
相比Collection,Map中加入了更多的方法,我們以HashMap為例來(lái)逐一探秘。了解Java7HashMap實(shí)現(xiàn)原理,將有助于理解下文。
forEach()
該方法簽名為void forEach(BiConsumer<? super K,? super V> action),作用是對(duì)Map中的每個(gè)映射執(zhí)行action指定的操作,其中BiConsumer是一個(gè)函數(shù)接口,里面有一個(gè)待實(shí)現(xiàn)方法void accept(T t, U u)。BinConsumer接口名字和accept()方法名字都不重要,請(qǐng)不要記憶他們。
需求:假設(shè)有一個(gè)數(shù)字到對(duì)應(yīng)英文單詞的Map,請(qǐng)輸出Map中的所有映射關(guān)系.
Java7以及之前經(jīng)典的代碼如下:
// 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()){ System.out.println(entry.getKey() + "=" + entry.getValue()); }
使用Map.forEach()方法,結(jié)合匿名內(nèi)部類,代碼如下:
// 使用forEach()結(jié)合匿名內(nèi)部類迭代Map HashMap<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); } });
上述代碼調(diào)用forEach()方法,并使用匿名內(nèi)部類實(shí)現(xiàn)BiConsumer接口。當(dāng)然,實(shí)際場(chǎng)景中沒(méi)人使用匿名內(nèi)部類寫法,因?yàn)橛蠰ambda表達(dá)式:
// 使用forEach()結(jié)合Lambda表達(dá)式迭代Map HashMap<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表達(dá)式?jīng)]關(guān)系,但是很有用。方法簽名為V getOrDefault(Object key, V defaultValue),作用是按照給定的key查詢Map中對(duì)應(yīng)的value,如果沒(méi)有找到則返回defaultValue。使用該方法程序員可以省去查詢指定鍵值是否存在的麻煩.
需求;假設(shè)有一個(gè)數(shù)字到對(duì)應(yīng)英文單詞的Map,輸出4對(duì)應(yīng)的英文單詞,如果不存在則輸出NoValue
// 查詢Map中指定的值,不存在時(shí)使用默認(rèn)值 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表達(dá)式?jīng)]關(guān)系,但是很有用。方法簽名為V putIfAbsent(K key, V value),作用是只有在不存在key值的映射或映射值為null時(shí),才將value指定的值放入到Map中,否則不對(duì)Map做更改.該方法將條件判斷和賦值合二為一,使用起來(lái)更加方便.
remove()
我們都知道Map中有一個(gè)remove(Object key)方法,來(lái)根據(jù)指定key值刪除Map中的映射關(guān)系;Java8新增了remove(Object key, Object value)方法,只有在當(dāng)前Map中key正好映射到value時(shí)才刪除該映射,否則什么也不做.
replace()
在Java7及以前,要想替換Map中的映射關(guān)系可通過(guò)put(K key, V value)方法實(shí)現(xiàn),該方法總是會(huì)用新值替換原來(lái)的值.為了更精確的控制替換行為,Java8在Map中加入了兩個(gè)replace()方法,分別如下:
replaceAll()
該方法簽名為replaceAll(BiFunction<? super K,? super V,? extends V> function),作用是對(duì)Map中的每個(gè)映射執(zhí)行function指定的操作,并用function的執(zhí)行結(jié)果替換原來(lái)的value,其中BiFunction是一個(gè)函數(shù)接口,里面有一個(gè)待實(shí)現(xiàn)方法R apply(T t, U u).不要被如此多的函數(shù)接口嚇到,因?yàn)槭褂玫臅r(shí)候根本不需要知道他們的名字.
需求:假設(shè)有一個(gè)數(shù)字到對(duì)應(yīng)英文單詞的Map,請(qǐng)將原來(lái)映射關(guān)系中的單詞都轉(zhuǎn)換成大寫.
Java7以及之前經(jīng)典的代碼如下:
// Java7以及之前替換所有Map中所有映射關(guān)系 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()方法結(jié)合匿名內(nèi)部類,實(shí)現(xiàn)如下:
// 使用replaceAll()結(jié)合匿名內(nèi)部類實(shí)現(xiàn) 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(); } });
上述代碼調(diào)用replaceAll()方法,并使用匿名內(nèi)部類實(shí)現(xiàn)BiFunction接口。更進(jìn)一步的,使用Lambda表達(dá)式實(shí)現(xiàn)如下:
// 使用replaceAll()結(jié)合Lambda表達(dá)式實(shí)現(xiàn) HashMap<Integer, String> map = new HashMap<>(); map.put(1, "one"); map.put(2, "two"); map.put(3, "three"); map.replaceAll((k, v) -> v.toUpperCase());
簡(jiǎn)潔到讓人難以置信.
merge()
該方法簽名為merge(K key, V value, BiFunction<? super V,? super V,? extends V> remappingFunction),作用是:
參數(shù)中BiFunction函數(shù)接口前面已經(jīng)介紹過(guò),里面有一個(gè)待實(shí)現(xiàn)方法R apply(T t, U u).
merge()方法雖然語(yǔ)義有些復(fù)雜,但該方法的用方式很明確,一個(gè)比較常見(jiàn)的場(chǎng)景是將新的錯(cuò)誤信息拼接到原來(lái)的信息上,比如:
map.merge(key, newMsg, (v1, v2) -> v1+v2);
compute()
該方法簽名為compute(K key, BiFunction<? super K,? super V,? extends V> remappingFunction),作用是把remappingFunction的計(jì)算結(jié)果關(guān)聯(lián)到key上,如果計(jì)算結(jié)果為null,則在Map中刪除key的映射.
要實(shí)現(xiàn)上述merge()方法中錯(cuò)誤信息拼接的例子,使用compute()代碼如下:
map.compute(key, (k,v) -> v==null ? newMsg : v.concat(newMsg));
computeIfAbsent()
該方法簽名為V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)
,作用是:只有在當(dāng)前Map中不存在key值的映射或映射值為null時(shí),才調(diào)用mappingFunction,并在mappingFunction執(zhí)行結(jié)果非null時(shí),將結(jié)果跟key關(guān)聯(lián).
Function是一個(gè)函數(shù)接口,里面有一個(gè)待實(shí)現(xiàn)方法R apply(T t).
computeIfAbsent()常用來(lái)對(duì)Map的某個(gè)key值建立初始化映射.比如我們要實(shí)現(xiàn)一個(gè)多值映射,Map的定義可能是Map<K,Set<V>>,要向Map中放入新值,可通過(guò)如下代碼實(shí)現(xiàn):
Map<Integer, Set<String>> map = new HashMap<>(); // Java7及以前的實(shí)現(xiàn)方式 if(map.containsKey(1)){ map.get(1).add("one"); }else{ Set<String> valueSet = new HashSet<String>(); valueSet.add("one"); map.put(1, valueSet); } // Java8的實(shí)現(xiàn)方式 map.computeIfAbsent(1, v -> new HashSet<String>()).add("yi");
使用computeIfAbsent()將條件判斷和添加操作合二為一,使代碼更加簡(jiǎn)潔.
computeIfPresent()
該方法簽名為V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction),作用跟computeIfAbsent()相反,即,只有在當(dāng)前Map中存在key值的映射且非null時(shí),才調(diào)用remappingFunction,如果remappingFunction執(zhí)行結(jié)果為null,則刪除key的映射,否則使用該結(jié)果替換key原來(lái)的映射.
這個(gè)函數(shù)的功能跟如下代碼是等效的:
// 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;
總結(jié)
看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝您對(duì)億速云的支持。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。