打开Hashtable的源码可以看见
1、Hashtable底层存放数据的其实是他的一个内部类的数组,在创建Hashtable实例的时候,需要指定这个数组的长度,默认是11,源码如下:
//无参构造方法 public Hashtable() { this(11, 0.75f); } public Hashtable(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity); if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal Load: "+loadFactor); if (initialCapacity==0) initialCapacity = 1; this.loadFactor = loadFactor; // table是具体放值的数组,设置长度是传入参数initialCapacity,期默认值是11 //Entity是一个被私有了的内部类 table = new Entry<?,?>[initialCapacity]; threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1); }
private static class Entry<K,V> implements Map.Entry<K,V> { final int hash; final K key; V value; Entry<K,V> next; protected Entry(int hash, K key, V value, Entry<K,V> next) { this.hash = hash; this.key = key; this.value = value; this.next = next; } @SuppressWarnings("unchecked") protected Object clone() { return new Entry<>(hash, key, value, (next==null ? null : (Entry<K,V>) next.clone())); } // Map.Entry Ops public K getKey() { return key; } public V getValue() { return value; } public V setValue(V value) { if (value == null) throw new NullPointerException(); V oldValue = this.value; this.value = value; return oldValue; } public boolean equals(Object o) { if (!(o instanceof Map.Entry)) return false; Map.Entry<?,?> e = (Map.Entry<?,?>)o; return (key==null ? e.getKey()==null : key.equals(e.getKey())) && (value==null ? e.getValue()==null : value.equals(e.getValue())); } public int hashCode() { return hash ^ Objects.hashCode(value); } public String toString() { return key.toString()+"="+value.toString(); } }
2、因为Hashtable是通过Entity数组存值的,且Entity有一个属性是专门放其数组中下一个值的Entity对象的,所以,查找起来更快,查找方法源码如下:
public synchronized V get(Object key) { Entry<?,?> tab[] = table; // 计算出key对应的hash值 int hash = key.hashCode(); // 使用key对应hash值算出一个数值来,使其在数组数组长度范围内,用来作为查找开的的索引 int index = (hash & 0x7FFFFFFF) % tab.length; for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) { if ((e.hash == hash) && e.key.equals(key)) { return (V)e.value; } } return null; }
3、通过put方法源码看到存值时只对value做了非空校验,且如果key对应的位置已经存有值,将会覆盖后返回老值
4、通过源码看如果找到了返回老值,没找到,返回null。
5、其线程安全的实现方式是在每个操作方法上都添加了synchronized同步锁,是的对其进行操作的时候要有获得锁和释放锁的操作,所以如果不需要线程安全最好不要用他。
6、java1.5之后,java为线程安全key&value集合提供了一个ConcurrentHashMap,它的实现思路和HashTable基本类似,不过在其基础上又做了很多优化。例如,他的同步锁不是加载方法上的,而是加在代码块上的,通过key计算hashcode的时候除了调用object的hashcode方法外还做了(h ^ (h >>> 16)) & HASH_BITS;位移后的与操作。
7、通过debug的时候发现,在实际使用hashTable时,java给我们加了层代理,而且不止java加了,不同的运行环境也有不同的处理。
打开代理方法都是被native关键字修饰了。而期创建的hashTable对象也是其子类Properties的对象