Map接口总结(如何使用默认方法)

Map接口总结(如何使用默认方法)

  • 基本操作

    get

    put(区别:Collection接口中添加为set)

    putAll

    remove

    containsKey

    containsValue

    size

    clear

  • 遍历操作

    keySet()

    values()

    entrySet()

  • 实现规约

    以下两个方法提示实现类需要重写用来比较相等的方法。

    equals

    hashcode

    以下两个方法是标准库的约定,但是Java中接口不支持定义构造器约束,只能保证标准库中都实现了这两个方法。

    构造器(空参数)

    构造器(Map other)

  • default方法

    排序在前的方法较为常用。

    默认方法是为了在标准库中添加默认支持函数式方法,同时也不必修改现有的类,这样做虽然有一定的好处,但是实际上在子类中使用default方法还是极有可能出错的,因为一个default方法无法满足所有的子类,也不可能满足。如果随意使用default方法,可能破坏了原有子类的一致性,产生意想不到的问题。

    在标准库中,除了并发相关类,比如ConcurrentHashMap等,一般没有问题。

    但是在其他类库的实现类中使用一定要慎之又慎,比如使用一个老版本的Map子类。

    引入default方法破坏了前向兼容性,容易产生运行时异常。

    V getOrDefault(Object key, V defaultValue) 常用,获取值或者默认值,可类比Optional.orElse。

    以下是先做判断,后计算的(if true)

    V putIfAbsent(K key, V value)

    V replace(K key, V value) 感觉叫putIfPresent更好

    boolean replace(K key, V oldValue, V newValue)

    boolean remove(Object key, Object value) 如果匹配的话,remove

    以下四个方法为一组,都是对entry(k, v)的更新,只是条件不一样。

    V computeIfAbsent(K key,
    				Function<? super K, ? extends V> mappingFunction)
    // 如果没有entry则更新
    // 返回值为value,可以用于后续操作
    // 常用于MultiMap,如下面一句话表示把用户添加到用户组里
    map.computeIfAbsent(userGroup, k -> new HashMap<User>()).add(user);
    
     V computeIfPresent(K key,
            BiFunction<? super K, ? super V, ? extends V> remappingFunction)
    // 如果有entry则更新
    
    V compute(K key,
            BiFunction<? super K, ? super V, ? extends V> remappingFunction)
    // 不常用,因为如果不包含entry则可能抛出空指针异常
    // 可以理解为同时支持上面两个方法,但是我们一般都需要对是否包含entry进行判断,所以不常用。
    // compute方法也可以实现merge这样的规约操作,既然如此,在需要规约操作时,我们为什么不用merge呢。
    // v 可能为空指针,程序员极有可能忘记检查,编译器也不能帮助检查。
    // 总之,这个compute方法不常用。
    
    // compute进行null判断
    map.compute(key, (k, v) -> (v == null) ? msg : v.concat(msg))
    // merge中更简单
    map.merge(key, msg, String::concat)
    
    V merge(K key, V value,
            BiFunction<? super V, ? super V, ? extends V> remappingFunction)
    // 没有则使用默认值,有则进行类似reduce的操作
    // 规约结果为null时,则删除
    // 好用
    // 常用来计数
    map.merge(key, 1, Integer::sum);
    
    void replaceAll(BiFunction<? super K, ? super V, ? extends V> function)
    // 使用泛型达到了更广的匹配
    // 通配符使用原则,使用的对象 (consumer) 使用 super,生成的对象 (provider) 使用 extends。
    

    forEach(BiConsumer<? super K, ? super V> action)

    !!!经常滥用的方法,不建议使用,action只建议使用简单的逻辑。每回看到别人使用forEach方法都感觉很恶心,这个方法看上去好用,可以传入(k,v)→ ... lambda表达式,但是函数式方法应该尽量不产生副作用,使用函数式方法的目的应该便于理解。然而在项目中经常看到大段的lambda表达式传入,在不便于调试的同时,还不能产生副作用。由于副作用的问题,每回想修改forEach的逻辑时,都必须改为for (Map.Entry<K, V> entry : map.entrySet()),然后再修改逻辑。

    包括Collections下的List,Set等都有滥用forEach方法。

    以下是我在网上找的一个例子,不知道你看到forEach后啥感觉,我反正是要吐了,况且这还是一段逻辑相对简单的代码。

    public static void main(String[] args)
        {
      
            // create a HashMap and add some values
            HashMap<Integer, String>
                map1 = new HashMap<>();
            map1.put(1, "Ram");
            map1.put(2, "Rohan");
            map1.put(3, "Shivam");
      
            HashMap<Integer, String>
                map2 = new HashMap<>();
            map2.put(1, "Tushar");
            map2.put(10, "Satya");
            map2.put(12, "Sundar");
      
            // print map details
            System.out.println("HashMap1: "
                               + map1.toString());
      
            System.out.println("HashMap2: "
                               + map2.toString());
      
            // provide value for new key which is absent
            // using computeIfAbsent method
            map2.forEach(
                (key, value)
                    -> map1.merge(
                        key,
                        value,
                        (v1, v2)
                            -> v1.equalsIgnoreCase(v2)
                                   ? v1
                                   : v1 + ", " + v2));
      
            // print new mapping
            System.out.println("New HashMap: " + map1);
        }
    

    总之,函数式方法使用的函数应该足够简单,便于理解。

    如果使用默认方法可以简化了理解,代码更简洁,而且没有副作用,确保代码兼容性,可以使用默认方法,其他情况下还是老老实实用命令式编程吧。

posted @ 2022-04-10 00:55  桦说编程  阅读(212)  评论(0编辑  收藏  举报