Hashtable与HashMap引发的血案

人物:
王小胖:性别:男。程序员,工作经验 1 year 。爱好:吃肉、电玩、马小花。特技:吃肉不用考虑胃的容量。
马小花:性别:女。学生,工作经验 0 year 。爱好:蛋糕、臭美、王小胖。特技:能够降服王小胖……
 
/**2011 年 2 月, 电影《将爱情进行到底》火得不得了。周末,小胖也陪着小花去看这部电影。放映中,小花被影片中的靖哥哥和杜拉拉感动的一沓糊涂,而小胖则心里暗自后悔没有 买一袋大爆米花来打发这无聊的时间。影片结束,小花已经是鼻涕一把泪一把,小胖也只有装模作样地抽动了几下鼻子,一心只想着一会儿是吃麦当劳还是必胜客。 */
回到家中,小胖和小花各自玩着电脑。
小花:胖子,你知道 Hashtable 和 HashMap 的区别吗?
小胖:略知。
小花:……装什么!!给我讲讲!!!
小胖:好的……
第一个区别就先来说说继承关系吧。
 如果你在baidu里google一下(技术类文章的搜索还是 推荐google),会发现网上的大致说法与“由于Java发展的历史原因。Hashtable是基于陈旧的Dictionary类的,HashMap是 Java 1.2引进的Map接口的一个实现。”相同。这种说法没有错,但是胖子觉得不够准确,特别是对于我们这种大众菜鸟来说,如果不去深究的话,可能就会造成一 些理解上的差异。简单的认为Hashtable 没有继承Map接口。胖子之前就犯过这样的错误(胖子承认自己笨,是真笨……) 。
       小花:那你怎么知道它们两个各自的继承关系呢?胖子。
我们可以参考一下最新的JDK1.6的源码,看看这两个类的定义:
public class Hashtable<K,V>
extends Dictionary<K,V>
implements Map<K,V>, Cloneable, java.io.Serializable {…}
public class HashMap<K,V>
extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {…}

 

可以看到hashtable也是继承了Map接口。它们的不同是 Hashtable(since JDK1.0)就继承了Dictionary这个抽象类,而HashMap(since JDK1.2)继承的则是AbstractMap这个抽象类。因为在Hashtable中看到继承Map后所实现的方法是JDK1.2版本时加上去的,所 以胖子猜想可能是在JDK 1.2开发时Sun工程师出于统一的考虑使得Hashtable也继承了Map接口。
       小花:哦,原来 JDK 源码还能看出来这个。
        小胖:……后面还能看出更多东西的。
        小花:好期待啊。
 
第二个区别我们从同步和并发性上来说说它们两个的不同。
 
可以通过这两个类得源码来分析,Hashtable中的主要方法 都做了同步处理,而HashMap则没有。可以说Hashtable在默认情况支持同步,而HashMap在默认情况下是不支持的。我们在多线程并发的环 境下,可以直接使用Hashtable,但是要使用HashMap的话就要自己增加同步处理了。对HashMap的同步处理可以使用 Collections类提供的synchronizedMap静态方法;或者直接使用JDK5.0之后提供的java.util.concurrent 包里的ConcurrentHashMap类。
小胖: synchronizedMap 静态方法和 ConcurrentHashMap 类我会以后再给你详细讲一下的。肥婆。
小花:你保证啊。钥匙忘了你知道后果的。
小胖:好的……
 
第三个区别就是它们对于 null 值的处理方式了。
 
我们依然能够从源代码中得知,Hashtable中,key和value都不允许出现null值。
 
 
 
复制代码
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
//
}
复制代码

 

 
 
 在我们使用上面的方法时,如参数value为null,可以从代码中直接看出程序会抛出NullPointerException;而在key为null时,则会在“int hash = key.hashCode();“这段计算Hash值的过程中抛出NullPointerException。 而 在在HashMap中,允许null作为key存在,并且和其他key的特性一样,这样的null值key只能有一个;另外HashMap允许多个 value为null。这样大家就要注意了, HashMap中就不能用get(key)方法来判断HashMap中是否存在某个key,因为value 为null和不存在该key的Entry都会返回null值,而应该用containsKey()方法来判断了。
 
 
 
复制代码
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
public class TestCase {
public static void main(String[] args) {
Map<Integer,String> hashMap = new HashMap<Integer,String>();
hashMap.put(0, null);
hashMap.put(1, "one");
hashMap.put(2, "two");
hashMap.put(null, "null");
for(Entry<Integer, String> e : hashMap.entrySet()) {
System.out.println("Key: " + e.getKey() + " -- Value: " + e.getValue());
}
System.out.println(hashMap.get(0));
System.out.println(hashMap.get(4));
System.out.println("Contains key 0 ? :" + hashMap.containsKey(0));
System.out.println("Contains key 4 ? :" + hashMap.containsKey(4));
System.out.println("Contains value null ? :" + hashMap.containsValue(null));
}
}
复制代码
复制代码
Key: null -- Value: null
Key: 0 -- Value: null
Key: 1 -- Value: one
Key: 2 -- Value: two
null
null
Contains key 0 ? :true
Contains key 4 ? :false
Contains value null ? :true
复制代码

 

HashMap对于null值key的处理网上有说 “null 用new Object()来代替,其Entry.hashCode=0,而且在取出的时候还会还回null的。”胖子我在读取源码的过程中看到了null值的 hash值确实是0 (内部实现的数组中的index也是),但是能力有限没有看到转为new Object()的过程。
小花: 原来 hashMap 的 containsKey 还有这么个陷阱,以后肥婆要小心了。
第四个不同就是它们两个 Hash 值的获取方式了。
 
还是通过源代码源代码,Hashtable是直接使用key对象的hash值。 
 
 
 
复制代码
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry tab[] = table;
int hash = key.hashCode();//hashcode
int index = (hash & 0x7FFFFFFF) % tab.length;
//
}
复制代码

 

而HashMap则是利用key对象的hash值重新计算一个新的hash值。
 
复制代码
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());//hashcode
int i = indexFor(hash, table.length);
//
}
static int hash(int h) {
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
复制代码

 

小花:胖子,都用了 hash 算法,你给我讲讲 Hash 算法吧。
小胖:嗯……以后的,今天我比较忙(其实是不会)。
小花:你是不是不会啊?嘿嘿(坏笑)。
小胖:什么不会……谈下一话题……
 
第五个不同就是 Hashtable 和 HashMap 它们两个内部实现方式的数组的初始大小和扩容的方式。
 
HashMap中内部数组的初始容量是16, 加载因子为0.75,而且数组容量增容后也要是2指数次幂:
复制代码
/**
* The default initial capacity - MUST be a power of two.
*/
static final int DEFAULT_INITIAL_CAPACITY = 16;
/**
* The load factor used when none specified in constructor.
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
复制代码

 

HashTable中的内部数组的初始容量是11,加载因子也是0.75数组的增容方式为(oldCapacity * 2 + 1):
 
 
复制代码
public Hashtable() {
this(11, 0.75f);
}
protected void rehash() {
int oldCapacity = table.length;
Entry[] oldMap = table;
int newCapacity = oldCapacity * 2 + 1;
//
}
复制代码

 

第六个不同我们从它们两个遍历方式的内部实现上来说。
Hashtable HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。
小花: Iterator 和 Enumeration 的区别是什么啊?给我讲讲。
小胖:我不是说我没有时间嘛,下回的。
小花:我都记下来,省得你给我混过去。(拿起笔开始记账中)
小胖:……(紧张)
第七个不同时它们的拷贝构造函数的不同。
 
依然是通过查看源码,可以发现它们两个对于拷贝函数初始容量的不同值。
HashMap的实现是:
 
public HashMap(Map<? extends K, ? extends V> m) {
this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
putAllForCreate(m);
}

 

而Hashtable的实现是:
public Hashtable(Map<? extends K, ? extends V> t) {
this(Math.max(2*t.size(), 11), 0.75f);
putAll(t);
}

 

小胖:今天讲的已经很多了。我有点饿了,肥婆。
小花:看你今天的表现这么好。走,带你去吃烤肉去。
小胖:哈哈,肥婆万岁。
深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值