Java8的這些集合騷操作,你掌握了嘛?

天喬巴夏丶發表於2020-12-13


Java8時Lambda表示式的出現,將行為作為引數傳遞進函式的函數語言程式設計,大大簡化了之前冗雜的寫法。

如果你對Lambda還不瞭解,可以參考我之前的關於Lambda表示式的總結:Java8的Lambda表示式,你會不?

對於集合一類,我們來整理一下發生的變化叭。

Iterable的forEach

Iterable介面就是所有可迭代型別的父介面,我們熟知的Collection介面就是繼承自它。Java8介面預設方法以及Lambda表示式的出現,讓我們在遍歷元素時對元素進行操作變得格外簡單。

下面的forEach方法就是Java8新增的,它接受一個Consumer物件,是一個消費者型別的函式式介面。

    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

下面這段程式碼遍歷輸出每個元素。

    public void testForEach(){
        List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
        list.forEach(System.out::println);
    }

Iterator的forEachRemaining

java.util.Iterator是用於遍歷集合的迭代器,介面定義如下:

public interface Iterator<E> {

    boolean hasNext();

    E next();

    default void remove() {
        throw new UnsupportedOperationException("remove");
    }

    default void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }
}

default方法是Java8介面中新增的,forEachRemaining方法接收一個Consumer,我們可以通過該方法簡化我們的遍歷操作:

    /**
     * Java8 為Iterator新增了 forEachRemaining(Consumer action) 方法
     */
    public static void main(String[] args) {

        List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
        Iterator<Integer> iterator = list.iterator();
        iterator.forEachRemaining(System.out::println);
    }

Collection的removeIf

Java8為Collection增加了預設的removeIf方法,接收一個Predicate判斷,test方法結果為true,就移除對應的元素。

    default boolean removeIf(Predicate<? super E> filter) {
        Objects.requireNonNull(filter);
        boolean removed = false;
        final Iterator<E> each = iterator();
        while (each.hasNext()) {
            // 判斷元素是否需要被移除
            if (filter.test(each.next())) {
                each.remove();
                removed = true;
            }
        }
        return removed;
    }

下面這段程式碼移除列表中的偶數元素。

    public void testRemoveIf() {
        List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
        list.removeIf(x -> x % 2 == 0);
        list.forEach(System.out::println);
    }

Stream操作

具體使用可以參照Java8的StreamAPI常用方法總結

    public void testStream(){
        IntStream stream = IntStream.builder().add(1).add(2).add(3).build();
        int max = stream.max().getAsInt();
        System.out.println(max);
    }

List的replaceAll

Java8為List介面增加了預設的replaceAll方法,需要UnaryOperator來替換所有集合元素,UnaryOperator是一個函式式介面。

    default void replaceAll(UnaryOperator<E> operator) {
        Objects.requireNonNull(operator);
        final ListIterator<E> li = this.listIterator();
        while (li.hasNext()) {
            li.set(operator.apply(li.next()));
        }
    }

下面這個示例為每個元素加上3。

    public void testReplaceAll(){
        List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
        list.replaceAll(x -> x + 3);
        list.forEach(System.out::println);
    }

List的sort

Java8為List介面增加了預設的sort方法,需要Comparator物件來控制元素排,我們可以傳入Lambda表示式。

    default void sort(Comparator<? super E> c) {
        Object[] a = this.toArray();
        Arrays.sort(a, (Comparator) c);
        ListIterator<E> i = this.listIterator();
        for (Object e : a) {
            i.next();
            i.set((E) e);
        }
    }

下面這個例子將list逆序。

    public void testSort() {
        List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
        list.sort((x, y) -> y - x);
        System.out.println(list);
    }

Map的ForEach

Map介面在Java8同樣也新增了用於遍歷的方法:

    default void forEach(BiConsumer<? super K, ? super V> action) {
        Objects.requireNonNull(action);
        for (Map.Entry<K, V> entry : entrySet()) {
            K k;
            V v;
            try {
                k = entry.getKey();
                v = entry.getValue();
            } catch(IllegalStateException ise) {
                // this usually means the entry is no longer in the map.
                throw new ConcurrentModificationException(ise);
            }
            action.accept(k, v);
        }
    }

該方法接收一個二元的引數,分別對應key和value:

    private void print(Map<Integer,String> map){
        map.forEach((x , y )-> {
            System.out.println("x -> " + x + ", y -> " + y);
        });
    }

為了接下來測試方便,資料先準備一下:

    Map<Integer, String> map = new HashMap<>();

    {
        map.put(1, "hello");
        map.put(2, "summer");
        map.put(3, "day");
        map.put(4, "tqbx");
    }

Map的remove

Java8新增了一個remove(Object key, Object value)方法。

    default boolean remove(Object key, Object value) {
        // key存在且key對應的value確實是傳入的value才移除
        Object curValue = get(key);
        if (!Objects.equals(curValue, value) ||
            (curValue == null && !containsKey(key))) {
            return false;
        }
        remove(key);
        return true;
    }

移除key為3且value為day的元素。

    @Test
    public void testRemove(){
        map.remove(3,"day");
        print(map);
    }

Map的compute相關方法

Object compute(Object key, BiFunction remappingFunction):該方法使用remappingFunction根據key-value對計算一個新value,情況如下:

  • 只要新value不為null,就使用新value覆蓋原value。
  • 如果原value不為null,但新value為null,則刪除原key-value對。
  • 如果原value、新value同時為null那麼該方法不改變任何key-value對,直接返回null。

Object computeIfAbsent(Object key, Function mappingFunction)

  • 如果傳給該方法的key引數在Map中對應的value為null,則使用mappingFunction根據key計算個新的結果。
  • 如果計算結果不為null,則用計算結果覆蓋原有的value。
  • 如果原Map原來不包括該key,那麼該方法可能會新增一組key-value對。

Object computeIfPresent(Object key, BiFunction remappingFunction)

  • 如果傳給該方法的key引數Map中對應的value不為null,該方法將使用remappingFunction根據原key、value計算一個新的結果。
  • 如果計算結果不為null,則使用該結果覆蓋原來的value。
  • 如果計算結果為null,則刪除原key-value對。
    @Test
    public void testCompute() {
        //key==2 對應的value存在時,使用計算的結果作為新value
        map.computeIfPresent(2, (k, v) -> v.toUpperCase());
        print(map);

        //key==6 對應的value為null (或不存在)時,使用計算的結果作為新value
        map.computeIfAbsent(6, (k) -> k + "haha");
        print(map);
    }

Map的getOrDefault

    default V getOrDefault(Object key, V defaultValue) {
        V v;
        // key存在或 value存在,則返回對應的value,否則返回defaultValue
        return (((v = get(key)) != null) || containsKey(key))
            ? v
            : defaultValue;
    }
    @Test
    public void testGetOrDefault(){
        // 獲取指定key的value,如果該key不存在,則返回default
        String value = map.getOrDefault(5, "如果沒有key==5的value,就返回這條資訊");
        System.out.println(value);
    }

Map的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);
        // 原值為null,新值則為傳入的value,不為null,則使用function計算,得到新值
        V newValue = (oldValue == null) ? value :
                   remappingFunction.apply(oldValue, value);
        // 新值如果為null,則移除原key-value對
        if(newValue == null) {
            remove(key);
        } else {
            // 不為null,則替換之
            put(key, newValue);
        }
        // 返回新值
        return newValue;
    }
    @Test
    public void testMerge(){
        // key為2 的值 加上 +add
        map.merge(2," + add",(oldval, newVal) -> oldval + newVal);
        print(map);
    }

Map的putIfAbsent

    default V putIfAbsent(K key, V value) {
        // 得到原值
        V v = get(key);
        // 原值為null,則替換新值
        if (v == null) {
            v = put(key, value);
        }
		// 返回原值
        return v;
    }
    @Test
    public void testPutIfAbsent(){
        // key = 10 對應的value不存在, 則用101010 覆蓋
        String s1 = map.putIfAbsent(10, "101010");
        System.out.println(s1);
        print(map);
        System.out.println("============================");
        // key = 2 對應的value存在且為summer,返回summer
        String s2 = map.putIfAbsent(2, "2222");
        System.out.println(s2);
        print(map);
    }

輸出結果

null
x -> 1, y -> hello
x -> 2, y -> summer
x -> 3, y -> day
x -> 4, y -> tqbx
x -> 10, y -> 101010
============================
summer
x -> 1, y -> hello
x -> 2, y -> summer
x -> 3, y -> day
x -> 4, y -> tqbx
x -> 10, y -> 101010

Map的replace相關方法

    @Test
    public void testReplace(){
        //boolean 將指定的 1 -> hello 鍵值對的value替換為hi
        map.replace(1,"hello","hi");
        print(map);
        System.out.println("============================");
        //V 指定key對應的value替換成新value
        map.replace(2,"天喬巴夏");
        print(map);
        System.out.println("============================");
        //void 對所有的key和value 進行計算,填入value中
        map.replaceAll((k,v)-> k + "-" + v.toUpperCase());
        print(map);
    }

輸出結果:

x -> 1, y -> hi
x -> 2, y -> summer
x -> 3, y -> day
x -> 4, y -> tqbx
============================
x -> 1, y -> hi
x -> 2, y -> 天喬巴夏
x -> 3, y -> day
x -> 4, y -> tqbx
============================
x -> 1, y -> 1-HI
x -> 2, y -> 2-天喬巴夏
x -> 3, y -> 3-DAY
x -> 4, y -> 4-TQBX

相關文章