8.1 集合工廠
如果我想創(chuàng)建一個集合,之前的做法是先new一個list,然后再一個個的add,這樣子有點繁瑣。
現(xiàn)在的方法可以這樣,是使用 Arrays.asList()工廠方法:List<String> friends = Arrays.asList("Raphael", "Olivia", "Thibaut");
但是這樣子創(chuàng)建只能更新,不可以增加、刪除。
8.1.1 List 工廠
工廠方法 List.of方法,創(chuàng)建的是一個只讀的列表。
它可以保護你的集合,以免被意外地修改。
建議是除非你需要進行某種形式的數(shù)據(jù)處理并對數(shù)據(jù)進行轉(zhuǎn)換,否則應(yīng)該盡量使用工廠方法。工廠方法使用起來更簡單,實現(xiàn)也更容易,并且在大多數(shù)情況下就夠用了。
8.1.2 Set 工廠
創(chuàng)建一個set集合 Set.ofSet<String> friends = Set.of("Raphael", "Olivia", "Thibaut");
8.1.3 Map 工廠
與list、set相比,創(chuàng)建map稍顯復(fù)雜,因為你需要同時傳遞鍵和值。Java 9中提供了兩種初始化一個不可變 Map 的方式。Map<String, Integer> ageOfFriends = Map.of("Raphael", 30, "Olivia", 25, "Thibaut", 26);
如果你只需要創(chuàng)建不到 10 個鍵值對的小型 Map,那么使用這種方法比較方便。
規(guī)模比較大的話,就使用Map.ofEntries 的工廠方法,該工廠方法接受以變長參數(shù)列表形式組織的 Map.Entry<K, V>對象作為參數(shù)
import static java.util.Map.entry;
Map<String, Integer> ageOfFriends
= Map.ofEntries(entry("Raphael", 30),
entry("Olivia", 25),
entry("Thibaut", 26));
System.out.println(ageOfFriends);
8.2 使用 List 和 Set
Java 8 在 List 和 Set 的接口中新引入了以下方法。
- removeIf 移除集合中匹配指定謂詞的元素。實現(xiàn)了 List 和 Set 的所有類都提供了該方法(事實上,這個方法繼承自 Collection 接口)。
- replaceAll 用于 List 接口中,它使用一個函數(shù)(UnaryOperator)替換元素。
- sort 也用于 List 接口中,對列表自身的元素進行排序。
8.2.1 removeIf 方法
循環(huán)的時候remove就會拋異常,ConcurrentModificationException。因為在底層實現(xiàn)上,for-each 循環(huán)使用了一個迭代器對象,集合由兩個不同的對象管理著
一般解決這個問題就需要顯示的調(diào)用Iterator 對象,并通過它調(diào)用 remove()方法
for (Iterator<Transaction> iterator = transactions.iterator();
iterator.hasNext(); ) {
Transaction transaction = iterator.next();
if(Character.isDigit(transaction.getReferenceCode().charAt(0))) {
iterator.remove();
}
}
不過這樣子的話,代碼就顯得繁瑣,所以就可以使用removeIf了,transactions.**removeIf**(transaction -> Character.isDigit(transaction.getReferenceCode().charAt(0)));
8.2.2 replaceAll 方法
List 接口提供的 replaceAll 方法讓你可以使用一個新的元素替換列表中滿足要求的每個元素。
原來的方式:
referenceCodes.stream()
.map(code -> Character.toUpperCase(code.charAt(0)) +
code.substring(1))
.collect(Collectors.toList())
.forEach(System.out::println);
輸入: [a12, C14, b13]
輸出: A12, C14, B13
或者
for (ListIterator<String> iterator = referenceCodes.listIterator(); iterator.hasNext(); ) {
String code = iterator.next();
iterator.set(Character.toUpperCase(code.charAt(0)) + code.substring(1));
}
現(xiàn)在可以這樣子:
referenceCodes.replaceAll(code -> Character.toUpperCase(code.charAt(0)) + code.substring(1));
8.3 使用 Map
Java 8 在 Map 接口中新引入了幾個默認(rèn)方法
8.3.1 forEach 方法
使用 Map.Entry<K, V>迭代器訪問 Map 集合中的每一個元素:
for(Map.Entry<String, Integer> entry: ageOfFriends.entrySet()) {
String friend = entry.getKey();
Integer age = entry.getValue();
System.out.println(friend + " is " + age + " years old");
}
從 Java 8 開始,Map 接口開始支持 forEach 方法,該方法接受一個 BiConsumer,以 Map的鍵和值作為參數(shù)。ageOfFriends.forEach((friend, age) -> System.out.println(friend + " is " + age + " years old"));
8.3.2 排序
Entry.comparingByValue 、Entry.comparingByKey這兩個可以幫助map排序
favouriteMovies
.entrySet()
.stream()
.sorted(Entry.comparingByKey())
.forEachOrdered(System.out::println);
8.3.3 getOrDefault 方法
當(dāng)獲取的對象不存在的話,就返回一個默認(rèn)值System.out.println(favouriteMovies.getOrDefault("Olivia", "Matrix"));
如果key為Olivia的值不存在,那么就返回"Matrix"
8.3.4 計算模式
就是如果key不存在,那么就執(zhí)行某個操作
- computeIfAbsent——如果指定的鍵沒有對應(yīng)的值(沒有該鍵或者該鍵對應(yīng)的值是空),那么使用該鍵計算新的值,并將其添加到 Map 中;
- computeIfPresent——如果指定的鍵在 Map 中存在,就計算該鍵的新值,并將其添加到 Map 中;
- compute——使用指定的鍵計算新的值,并將其存儲到 Map 中。
computeIfAbsent 的一個應(yīng)用場景是緩存信息。
// 如果key不存在就執(zhí)行calculateDigest操作
lines.forEach(line -> dataToHash.computeIfAbsent(line, this::calculateDigest));
還有這個場景,
String friend = "Raphael";
List<String> movies = friendsToMovies.get(friend);
if(movies == null) {
movies = new ArrayList<>();
friendsToMovies.put(friend, movies);
}
movies.add("Star Wars");
優(yōu)化
friendsToMovies.computeIfAbsent("Raphael", name -> new ArrayList<>())
.add("Star Wars");
8.3.5 刪除模式
java提供了一個重載方法的remove方法。
可能是這樣子刪除的:判斷map的key存在,再比較值是否相等,然后再刪除
String key = "Raphael";
String value = "Jack Reacher 2";
if (favouriteMovies.containsKey(key) &&
Objects.equals(favouriteMovies.get(key), value)) {
favouriteMovies.remove(key);
return true;
} else {
return false;
}
現(xiàn)在這樣:favouriteMovies.remove(key, value);
vlaue小于10就刪除movies.entrySet().removeIf(entry -> entry.getValue() < 10);
8.3.6 替換模式
Map 中提供了兩種新的方法來替換其內(nèi)部映射項,分別是:
- replaceAll——通過 BiFunction 替換 Map 中每個項的值。該方法的工作模式類似于之前介紹過的 List 的 replaceAll 方法;
- Replace——如果鍵存在,就可以通過該方法替換 Map 中該鍵對應(yīng)的值。它是對原有replace 方法的重載,可以僅在原有鍵對應(yīng)某個特定的值時才進行替換。
movie 就是valuefavouriteMovies.replaceAll((friend, movie) -> movie.toUpperCase());
8.3.7 merge 方法
Map<String, String> friends = Map.ofEntries(entry("Raphael", "Star Wars"));
Map<String, String> everyone = new HashMap<>(family);
everyone.putAll(friends);
System.out.println(everyone);
但是如果是有沖突的合并需要處理,可以這樣
Map<String, String> family = Map.ofEntries(
entry("Teo", "Star Wars"), entry("Cristina", "James Bond"));
Map<String, String> friends = Map.ofEntries(
entry("Raphael", "Star Wars"), entry("Cristina", "Matrix"));
Map<String, String> everyone = new HashMap<>(family);
friends.forEach((k, v) ->
everyone.merge(k, v, (movie1, movie2) -> movie1 + " & " + movie2)); // 如果存在重復(fù)的鍵,就連接兩個值
System.out.println(everyone);
merge 方法處理空值的方法相當(dāng)復(fù)雜,在 Javadoc 文檔中是這么描述的:
如果指定的鍵并沒有關(guān)聯(lián)值,或者關(guān)聯(lián)的是一個空值,那么[merge]會將它關(guān)聯(lián)到指定的非空值。否則,[merge]會用給定映射函數(shù)的[返回值]替換該值,如果映射函數(shù)的返回值為空就刪除[該鍵]。
8.4 改進的 ConcurrentHashMap
并發(fā)安全,使用的是分段鎖
8.4.1 歸約和搜索
ConcurrentHashMap 類支持三種新的操作
- forEach——對每個(鍵, 值)對執(zhí)行指定的操作;
- reduce——依據(jù)歸約函數(shù)整合所有(鍵, 值)對的計算結(jié)果;
- search——對每個(鍵, 值)對執(zhí)行一個函數(shù),直到函數(shù)取得一個非空值。
每種操作支持四種形式的參數(shù),接受函數(shù)使用鍵、值、Map.Entry 以及(鍵, 值)對作為參數(shù):
- 使用鍵(forEachKey,reduceKeys,searchKeys);
- 使用值(forEachValue,reduceValues,searchValues);
- 使用 Map.Entry 對象(forEachEntry,reduceEntries,searchEntries);
- 使用鍵和值(forEach,reduce,search)。
ConcurrentHashMap<String, Long> map = new ConcurrentHashMap<>();
long parallelismThreshold = 1;
Optional<Integer> maxValue =
Optional.ofNullable(map.reduceValues(parallelismThreshold, Long::max));
8.4.2 計數(shù)
ConcurrentHashMap 類提供了一個新的 mappingCount 方法,能以長整形 long 返回 Map中的映射數(shù)目。你應(yīng)該盡量在新的代碼中使用它,而不是繼續(xù)使用返回 int 的 size 方法。
文章來源:http://www.zghlxwxcb.cn/news/detail-423207.html
8.4.3 Set 視圖
ConcurrentHashMap 類還提供了一個新的 keySet 方法,該方法以 Set 的形式返回ConcurrentHashMap 的一個視圖(Map 中的變化會反映在返回的 Set 中,反之亦然)。
文章來源地址http://www.zghlxwxcb.cn/news/detail-423207.html
8.5 小結(jié)
- Java 9 支持集合工廠,使用 List.of、Set.of、Map.of 以及 Map.ofEntries 可以創(chuàng)建小型不可變的 List、Set 和 Map。
- 集合工廠返回的對象都是不可變的,這意味著創(chuàng)建之后你不能修改它們的狀態(tài)。
- List 接口支持默認(rèn)方法 removeIf、replaceAll 和 sort。
- Set 接口支持默認(rèn)方法 removeIf。
- Map 接口為常見模式提供了幾種新的默認(rèn)方法,并降低了出現(xiàn)缺陷的概率。
- ConcurrentHashMap 支持從 Map 中繼承的新默認(rèn)方法,并提供了線程安全的實現(xiàn)。
到了這里,關(guān)于《Java8實戰(zhàn)》第8章 Collection API 的增強功能的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!