Java中的函數式編程(五)Java集合框架中的高階函數
寫在前面
Java 8中的集合框架
|
介面名
|
Java8新加入的方法
|
|
Collection
|
forEach(), removeIf(), stream(), parallelStream()
|
|
List
|
replaceAll(), sort()
|
|
Map
|
forEach(), replaceAll(), compute(), computeIfAbsent(), computeIfPresent(), merge()
|
一個函數,如果它有一個或多個參數是函數類型的,或者它的返回值是函數類型的,那麼我們稱這個函數為高階函數。
public class Container implements Collection { ... }
升級到Java 8後,因為Collection中新增了方法 forEach,假設沒有默認方法,之前的類 Container 將編譯失敗。
Collection中的高階函數
1. forEach
default void forEach(Consumer<? super T> action) { Objects.requireNonNull(action); for (T t : this) { action.accept(t); } }
它的作用是遍歷集合中的每一個元素,然後依次對每個元素執行參數 action 指定的動作。參數 action 是一個 Consumer 類型的函數式介面,我們可以通過lambda表達式或方法引用來實現一個 Consumer 。因此,在Java 8或以後的版本中,不要用 for 語句來迭代 Collection 了,不妨使用 forEach 方法。
示例程式碼如下:
public static void collectionForEach() { Collection<String> list = Arrays.asList("Guangdong", "Zhejiang", "Jiangsu"); // for 語句 for (String s : list) { System.out.println(s); } // forEach 方法 + lambda表達式 list.forEach(s -> System.out.println(s)); // forEach 方法 + 方法引用 list.forEach(System.out::println); }
2. removeIf
default boolean removeIf(Predicate<? super E> filter) { ... }
removeIf的作用是遍歷集合中的每一個元素,然後依次對每個元素進行指定的過濾操作。filter 參數是一個 Predicate 類型的函數式介面。
示例程式碼如下,假定我們要過濾掉以字母 G 開頭的省份:
public static void collectionRemoveIf() { List<String> provinces = new ArrayList<>(Arrays.asList("Guangdong", "Jiangsu", "Guangxi", "Jiangxi", "Shandong")); boolean removed = provinces.removeIf(s -> { return s.startsWith("G"); }); System.out.println(removed); System.out.println(provinces); }
上述程式碼輸出為:
3. replaceAll
default void replaceAll(UnaryOperator<E> operator) { ... }
replaceAll方法的作用是對集合中的每個元素執行 operator 指定的計算,並用計算結果替換原來的元素。參數 operator 是類型為 UnaryOperator 的函數式介面,它的參數和返回值類型是相同的。
public static void listReplaceAll() { List<String> provinces = Arrays.asList("Guangdong", "Jiangsu", "Guangxi", "Jiangxi", "Shandong"); provinces.replaceAll(s -> s.toUpperCase()); System.out.println(provinces); }
上述程式碼的輸出為:
4. sort
default void sort(Comparator<? super E> c) { ... }
sort方法是根據比較器 c 指定的排序規則,對 List 中的元素進行排序。參數 c 的類型是Comparator,同樣是一個函數式介面。
public static void listSort() { List<String> list = Arrays.asList("Guangdong", "Zhejiang", "Jiangsu", "Xizang", "Fujian", "Hunan", "Guangxi"); // 對省份進行排序,首先按照長度排序,如果長度一樣,則按照字母順序排序 list.sort((first, second) -> { int lenDiff = first.length() - second.length(); return lenDiff == 0 ? first.compareTo(second) : lenDiff; }); list.forEach(s -> System.out.println(s)); }
上述程式碼的輸出為:
5. stream 和 parallelStream
Map中的高階函數
1. forEach
default void forEach(BiConsumer<? super K, ? super V> action) { ... }
可以看到,Map的forEach方法的作用是遍歷Map中所有的鍵值對,並執行參數 action 指定的操作。參數 action 的類型是函數式介面 BiConsumer,要求有2個參數,分別代表鍵值對的key和value。
public static void mapForEach() { Map<String, String> cityMap = new HashMap<>(); cityMap.put("Guangdong", "Guangzhou"); cityMap.put("Zhejiang", "Hangzhou"); cityMap.put("Jiangsu", "Nanjing"); cityMap.forEach((key, value) -> { System.out.println(String.format("%s 的省會是 %s", key, value)); }); }
上述程式碼的輸出為:
2. replaceAll
default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) { ... }
public static void mapReplaceAll() { Map<String, String> cityMap = new HashMap<>(); cityMap.put("Guangdong", "Guangzhou"); cityMap.put("Zhejiang", "Hangzhou"); cityMap.put("Jiangsu", "Nanjing"); // 將省府的拼音轉換為大寫 cityMap.replaceAll((key, value) -> { return value.toUpperCase(); }); cityMap.forEach((key, value) -> { System.out.println(String.format("%s 的省會大寫是 %s", key, value)); }); }
3. compute
default V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) { ... }
示例程式碼如下:
public static void mapCompute() { Map<String, String> cityMap = new HashMap<>(); cityMap.put("Guangdong", "null"); cityMap.put("Zhejiang", "Hangzhou"); cityMap.put("Jiangsu", "null"); // 稍顯複雜的語句,先調用 forEach 遍歷 cityMap 中的鍵,然後根據原有的鍵值對計算新的值 Set keys = new HashSet<>(cityMap.keySet()); keys.forEach(key -> { cityMap.compute(key, (k, v) -> { // 如果是 Guangdong,則返回 Guangzhou if ("Guangdong".equals(k)) { return "Guangzhou"; } // 如果舊的鍵值對中,值是字元串 "null" ,則返回 null。 // 這意味著 cityMap 會刪除對應的key if ("null".equals(v)) { return null; } // 否則,返回原來的 value 值 return v; }); }); cityMap.forEach((key, value) -> { System.out.println(String.format("%s 的省會是 %s", key, value)); }); }
上述程式碼的輸出為:
4. computeIfPresent
default V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) { Objects.requireNonNull(remappingFunction); V oldValue; if ((oldValue = get(key)) != null) { V newValue = remappingFunction.apply(key, oldValue); if (newValue != null) { put(key, newValue); return newValue; } else { remove(key); return null; } } else { return null; } }
5. computeIfAbsent
default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) { Objects.requireNonNull(mappingFunction); V v; if ((v = get(key)) == null) { V newValue; if ((newValue = mappingFunction.apply(key)) != null) { put(key, newValue); return newValue; } } return v; }
public static void mapComputeIfAbsent() { Map<String, Integer> staffMap = new HashMap<>(); staffMap.put("Lilei", 24); staffMap.put("Hanmeimei", 22); staffMap.put("Liming", 24); staffMap.put("Jim", 22); staffMap.put("David", 24); Map<Integer, List<String>> staffInvertMap = new HashMap<>(); staffMap.forEach((key, value) -> { // 以年齡為鍵,構建一個新的Map // 以 22 歲為例: // 如果 staffInvertMap 之前不存在 22 歲對應的映射關係, // 則新建一個 "年齡 -> ArrayList<String>" 的映射,並且把新建的 ArrayList 返回。 // 如果 staffInvertMap 已經存在 22 歲對應的映射關係了,則將已存在的 ArrayList 返回。 List<String> nameList = staffInvertMap.computeIfAbsent(value, age -> { // 對於同一個 age,這句程式碼只會執行一次 return new ArrayList<>(); }); nameList.add(key); }); System.out.println(staffInvertMap); }
6. merge
default V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) { Objects.requireNonNull(remappingFunction); Objects.requireNonNull(value); V oldValue = get(key); V newValue = (oldValue == null) ? value : remappingFunction.apply(oldValue, value); if(newValue == null) { remove(key); } else { put(key, newValue); } return newValue; }
public static void mapMerge() { Map<String, String> staffMap = new HashMap<>(); staffMap.put("Lilei", "性別男 "); // 等價於 staffMap.merge("Lilei", "年齡 24", String::concat) // oldValue 即之前添加的值 「性別男 」,value 即merge函數的第二個參數 「年齡24」 staffMap.merge("Lilei", " 年齡24", (oldValue, value) -> oldValue.concat(value)); staffMap.merge("Hanmeimei", "年齡22", String::concat); System.out.println(staffMap); }

