结合JDK源码看设计模式——组合模式

前言:

  相信大家都打开过层级很多很多的文件夹。如果把第一个文件夹看作是树的根节点的话,下面的子文件夹就可以看作一个子节点。不过最终我们寻找的还是文件夹中的文件,文件可以看做是叶子节点。下面我们介绍一种模式,与这种树级结构息息相关。当然,今天的主角是HashMap。接下来我们一起来看HashMap中到底是怎么跟树级结构进行挂钩的。

一、定义

  将对象组合成树形结构以表示“部分-整体”的一个层次结构,使客户端对单个对象和组合对象保持一致的方式处理。

二、适用场景

1、客户端可以忽略组合对象与单个对象的差异

  注意组合模式中的概念,当客户端使用组合模式的时候是对单个对象和组合对象保持一致的方式处理,而不是不同的方式处理。所以如果客户端可以忽略组合对象和单个对象的差异时,才用组合模式。

2、处理一个树形结构

  这里就很好理解了,组合模式就是处理树形结构的

三、结合HashMap看组合模式

  1、抽象构件:总的抽象类或者接口,定义一些通用的方法,比如新增、删除。

  2、中间构件:继承或者实现抽象构件,定义存储方式,并针对特殊需要重写抽象构件的方法。

  3、叶子节点:继承或者实现抽象构件,并针对特殊需要重写抽象构件的方法。

  叶子节点需要实现或者继承抽象构件,如果有需要也可以添加到中间构件上。就比如说我现在进入D盘,D盘就是一个抽象构件,无论在D盘哪里,我都可以执行新建,删除操作。中间构件就相当于文件夹,里面定义了相应的存储方式,加入的文件是叶子节点,并且会按照这种方式来存储,同时这个中间构件也可以选择再加入一个其他中间构件或者自己。叶子节点就可以看作文件。注意,在组合模式下所有类都需要直接或者间接,继承或实现总的抽象构件。下面讲HashMap中的putAll()方法

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {
    public void putAll(Map<? extends K, ? extends V> m) {
        putMapEntries(m, true);
    }
    final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
        int s = m.size();
        if (s > 0) {
            if (table == null) { // pre-size
                float ft = ((float)s / loadFactor) + 1.0F;
                int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                         (int)ft : MAXIMUM_CAPACITY);
                if (t > threshold)
                    threshold = tableSizeFor(t);
            }
            else if (s > threshold)
                resize();
            for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
                K key = e.getKey();
                V value = e.getValue();
                putVal(hash(key), key, value, false, evict);
            }
        }
    }
}

  上面是简化的HashMap。我们看见putAll方法传入的是Map对象,Map就是一个抽象构件(同时这个构件中只支持键值对的存储格式),而HashMap是一个中间构件,HashMap中的Node节点就是叶子节点。说到中间构件就会有规定的存储方式。HashMap中的存储方式是一个静态内部类的数组Node<K,V>[] tab。下面是简化的具体代码

public class HashMap<K,V> extends AbstractMap<K,V>
    implements Map<K,V>, Cloneable, Serializable {
    static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;

        Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }
  
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}
}

  所以我们调用put方法实际上是加入一个叶子节点,而我们调用putAll相当于把其他Map下面的文件夹中的文件拷贝过来。还有Hashtable也可以看作一个中间构件,里面的putAll方法同样是适合放入实现Map接口的对象。这里如果将Hashtable的这个中间构件放入HashMap中,那么我们这些方法还能用吗。答案是能用,组合模式处理的就是一致性的问题。Map接口中的方法,比如put(),remove(),到下面的中间构件中其实都是一致的功能,不过就是不同的中间构件中的处理方式可能会有细微的差别。下面是我的测试代码

public class Test {

    public static void main(String[] args) {
  Map<Integer,String> rootmap=new HashMap<Integer,String>();  rootmap.put(1,"HashMap文件1");  Map<Integer,String> map1=new Hashtable<Integer,String>();  map1.put(2,"Hashtable文件1");  map1.put(3,"Hashtable文件2");  map1.put(4,"Hashtable文件3");  rootmap.putAll(map1);  System.out.println(rootmap);
} }

输出结果为:

  上面实现将Hashtable中的键值对放入HashMap中。如果放在组合模式下,你就可以看作是将Hashtable这个中间构件下的文件拷贝到HashMap这个中间构件中。在我们电脑里文件任何类型的文件都可以拷到其他任意地方。但是用来理解Map这个抽象构件的就不行,因为实现Map接口的类只支持放入键值对格式的叶子节点,如果不是(比如说ArrayList)就不行,这是底层方法不支持。同时我们回到组合模式上重要的一点:一致性,Map下面添加都是put方法,需要传入两个值,List下都是add方法,只需要传入一个值,如果List中的可以通过调用putAll方法放入Map里,那么我该怎么给List赋key或value呢。(不是说不能有put(1,list)这样的操作,而是说在组合模式下操作时候需要有一致性,并且这里说的组合模式针对的是putAll方法,与其他方法无关)

四、总结

  这里只是解析了HashMap中的putAll组合模式。在平常写代码的过程中要使用组合模式,就需要先定义一个抽象类或者接口。在这里就可以看作是抽象构件,主要实现一些基本的通用的功能。接着如果有需要就要定义一个中间构件,并且实现抽象构件,里面的方法可以根据特殊需要重写。而且这个类最重要的是要有存储结构在里面,用Map存储,或者用List存储等等都可以。接着就是我们真正的叶子节点,同样是继承或实现抽象构件,里面的方法可以根据特殊需要重写。当你看完这篇博客,你可以再去看定义和适用场景,说不定收获会更大。

(0)

相关推荐

  • Java基础知识总结(异常机制、集合、JUC、IO)

    Java基础知识总结(异常机制、集合、JUC、IO)

  • 结合JDK源码看设计模式——迭代器模式

    前言: Iterator翻译过来就是迭代器的意思.在前面的工厂模式中就介绍过了iterator,不过当时介绍的是方法,现在从Iterator接口的设计来看,似乎又是一种设计模式,下面我们就来讲讲迭代器 ...

  • 结合JDK源码看设计模式——原型模式

    定义: 指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.不需要知道任何创建的细节,不调用构造函数 适用场景: 类初始化的时候消耗较多资源 new产生的对象需要非常繁琐的过程 构造函数比 ...

  • 结合JDK源码看设计模式——观察者模式

    前言: 现在我们生活中已经离不开微信,QQ等交流软件,这对于我们来说不仅是交流,更有在朋友圈中或空间中进行分享自己的生活,同时也可以通过这个渠道知道别人的生活.我们在看朋友圈的时候其实我们扮演的就是一 ...

  • 结合Mybatis源码看设计模式——外观模式

    定义 提供了一个统一的接口,用来访问子系统中一群接口 适用场景 子系统复杂,增加外观模式提供简单调用接口 构建多层系统结构,用外观对象作为每层入口 详解 外观模式,主要理解外观.通俗一点可以认为这个模 ...

  • 结合JDK源码看设计模式——单例模式

    定义: 保证一个类仅有一个实例,并提供一个全局访问点 适用场景: 确保任何情况下这个对象只有一个实例 详解: 私有构造器 单利模式中的线程安全+延时加载 序列化和反序列化安全, 防止反射攻击 结合JD ...

  • 电商平台搭建,电商源码开发,开发模式与中心点

    相比传统的单一购买方式不同,消费者对于产品带来的社交属性更加重视.在这种需求的驱使下,以及社群经济.网红经济.微商,自媒体经济大热的环境的催生下,直播电商就此诞生了. 一般来说,开发商城平台有三种方式 ...

  • JDK源码之ArrayList-Iterator

    之前写过一篇ArrayList源码的博客 https://www.cnblogs.com/zumengjie/p/13538394.html 其中遗留了一个问题,ArrayList添加元素和删除元素或 ...

  • 简析相亲交友源码的系统开发模式

    社交产品分为两种,一是基于社交关系链分发信息,包括IM.SNS.社交媒体:二是基于某种需求的社交匹配市场,比如约会为目的的陌生人社交,以招聘为需求的职业社交.前者追求关系链的密度和强度,讲究的是信息分 ...

  • C#设计模式-组合模式(Composite Pattern)

    概念 组合是一种结构型设计模式, 你可以使用它将对象组合成树状结构, 并且能像使用独立对象一样使用它们. 组合模式(Composite Pattern)是将对象组合成树形结构以表示'部分-整体'的层次 ...