结合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存储等等都可以。接着就是我们真正的叶子节点,同样是继承或实现抽象构件,里面的方法可以根据特殊需要重写。当你看完这篇博客,你可以再去看定义和适用场景,说不定收获会更大。
结合JDK源码看设计模式——组合模式的更多相关文章
- 结合JDK源码看设计模式——桥接模式
前言: 在我们还没学习框架之前,肯定都学过JDBC.百度百科对JDBC是这样介绍的[JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Jav ...
- 结合JDK源码看设计模式——原型模式
定义: 指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.不需要知道任何创建的细节,不调用构造函数适用场景: 类初始化的时候消耗较多资源 new产生的对象需要非常繁琐的过程 构造函数比较 ...
- 结合JDK源码看设计模式——模板方法模式
前言: 相信很多人都听过一个问题:把大象关进冰箱门,需要几步? 第一,把冰箱门打开:第二,把大象放进去:第三,把冰箱门关上.我们可以看见,这个问题的答案回答的很有步骤.接下来我们介绍一种设计模式--模 ...
- 结合JDK源码看设计模式——迭代器模式
前言: Iterator翻译过来就是迭代器的意思.在前面的工厂模式中就介绍过了iterator,不过当时介绍的是方法,现在从Iterator接口的设计来看,似乎又是一种设计模式,下面我们就来讲讲迭代器 ...
- 结合JDK源码看设计模式——建造者模式
概念: 将一个复杂对象的构建与它的表示分离.使得同样构建过程可以创建不同表示适用场景: 一个对象有很多属性的情况下 想把复杂的对象创建和使用分离 优点: 封装性好,扩展性好 详解: 工厂模式注重把这个 ...
- 结合JDK源码看设计模式——策略模式
前言: 现在电商已经成为我们生活中不可或缺的购物渠道,同时各大商家会针对不同的时间做出不同的折扣,这在我们看来就是一种营销手段,也是一种策略,今天我们就来讲讲JDK中的策略模式是怎么样的. 一.定义 ...
- 结合JDK源码看设计模式——单例模式
定义: 保证一个类仅有一个实例,并提供一个全局访问点 适用场景: 确保任何情况下这个对象只有一个实例 详解: 私有构造器 单利模式中的线程安全+延时加载 序列化和反序列化安全, 防止反射攻击 结合JD ...
- 结合JDK源码看设计模式——简单工厂、工厂方法、抽象工厂
三种工厂模式的详解: 简单工厂模式: 适用场景:工厂类负责创建的对象较少,客户端只关心传入工厂类的参数,对于如何创建对象的逻辑不关心 缺点:如果要新加产品,就需要修改工厂类的判断逻辑,违背软件设计中的 ...
- 结合JDK源码看设计模式——享元模式
前言 在说享元模式之前,你一定见到过这样的面试题 public class Test { public static void main(String[] args) { Integer a=Inte ...
随机推荐
- 条件随机场CRF(二) 前向后向算法评估标记序列概率
条件随机场CRF(一)从随机场到线性链条件随机场 条件随机场CRF(二) 前向后向算法评估标记序列概率 条件随机场CRF(三) 模型学习与维特比算法解码 在条件随机场CRF(一)中我们总结了CRF的模 ...
- 0511JS流程练习
一.输入三个数,判断大小 var one = prompt("请输入第一个数"); var two = prompt("请输入第二个数"); var three ...
- WordPress调用特色图片地址源,去除特色图片img标签其他样式
我们在制作WordPress主题时候想要给wordpress特色图片,这也是为了更加的美观,但是我们直接使用wordpress特色图片引用代码的时候却发现,出现下面的情况. wordpress特色图片 ...
- DX11 Without DirectX SDK--02 渲染一个三角形
回到 DirectX11--使用Windows SDK来进行开发 目前暂时没有写HLSL具体教程的打算,而是着重于如何做到不用DirectX SDK来进行渲染.除此之外,这里也没有使用Effects框 ...
- CXF整合spring
近公司需要弄webservics,还说不用框架整合(提倡使用hessian,他们既然说与操作系统有兼容问题,由于人员单薄,不得不屈服,哎),我想了老半天没弄明白他说的不用框架整合spring,尝试过直 ...
- 如何提高缓存命中率(Redis)
缓存命中率的介绍 命中:可以直接通过缓存获取到需要的数据. 不命中:无法直接通过缓存获取到想要的数据,需要再次查询数据库或者执行其它的操作.原因可能是由于缓存中根本不存在,或者缓存已经过期. 通常来讲 ...
- PAT1058:A+B in Hogwarts
1058. A+B in Hogwarts (20) 时间限制 50 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue If you ...
- tkinter中checkbutton多选框控件和variable用法(六)
checkbutton控件 简单的实现多选: import tkinter wuya = tkinter.Tk() wuya.title("wuya") wuya.geometry ...
- Python3 requests与http.cookiejar的使用(cookie的保存与加载)
在学习Python之余,发现Python2与Python3 有很大的变化,之前使用urllib和cookielib来保存cookie,发现很繁琐,于是改用requests.发现cookielib在3. ...
- 关于xpath语句完全正确,但是页面报错: no such element: Unable to locate element: {"method":"xpath","selector":"xpath"}
之前使用selenium-webdriver来写UI的自动化脚本,发现有一个元素一直无法定位,查看其源码,如下 利用xpathChecker验证了xpath语句的是正确的,但是控制台一直报错: no ...