本文学习HashTable,这是一个古老类,基本上被废除,但是学习它有助于理解HashMap。
HashTable简介
HashTable是一个key-value键值对的数据类型,较为古老,现已基本废除不再使用,使用synchronized保证同步,效率较低,一般单线程转为使用HashMap,多线程使用ConcurrentHashMap。
但学习其实现方式有助于理解hashMap的实现。
HashTable继承关系
继承Dictionary类,实现Map接口。
HashTable原理
hashTable的原理和hashMap大致相同。在一些算法和设计方面,hashTable比hashMap低下。
HashTable属性
//节点数组private transient Entry<?,?>[] table;//节点个数private transient int count;//临界值private int threshold;//加载因子 0.75private float loadFactor;//模数,hashTable被修改的次数private transient int modCount = 0;
HashTable插入元素
当key值重复,就覆盖并返回旧的value
当key值不重复,直接插入。如果出现冲突,拉链发解决
一、(hash & 0x7FFFFFFF) % tab.length;
取余操作,获取散列下标,相比于hashMap的一次位运算,这里效率很低。
这里巧妙在于,与0x7FFFFFFF进行与运算,避免负数的出现
public synchronized V put(K key, V value) { // Make sure the value is not null //不允许插入null值 if (value == null) { throw new NullPointerException(); } // Makes sure the key is not already in the hashtable. Entry<?,?> tab[] = table; //这里并没有对key进行为空检测,是不合理的 int hash = key.hashCode(); //hash值与0x7FFFFFFF按位与,防止出现负数。 //与tab.length做取余操作,得到散列下标 int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entry<K,V> entry = (Entry<K,V>)tab[index]; //不为空 for(; entry != null ; entry = entry.next) { //出现重复则覆盖 if ((entry.hash == hash) && entry.key.equals(key)) { V old = entry.value; entry.value = value; return old; } } //出现冲突、或对应下标节点为空,插入元素 addEntry(hash, key, value, index); return null;}
addEntry方法
如果节点个数超过临界值,就扩容。
否则直接插入元素,或拉链法解决冲突
private void addEntry(int hash, K key, V value, int index) { modCount++; Entry<?,?> tab[] = table; if (count >= threshold) { // Rehash the table if the threshold is exceeded rehash(); tab = table; hash = key.hashCode(); index = (hash & 0x7FFFFFFF) % tab.length; } // Creates the new entry. @SuppressWarnings("unchecked") Entry<K,V> e = (Entry<K,V>) tab[index]; tab[index] = new Entry<>(hash, key, value, e); count++;}
rehash()方法
这里的扩容方法,只是简单的将节点散列到新的数组上,并没有做将链表变短的操作。
protected void rehash() { int oldCapacity = table.length; Entry<?,?>[] oldMap = table; // overflow-conscious code int newCapacity = (oldCapacity << 1) + 1; if (newCapacity - MAX_ARRAY_SIZE > 0) { if (oldCapacity == MAX_ARRAY_SIZE) // Keep running with MAX_ARRAY_SIZE buckets return; newCapacity = MAX_ARRAY_SIZE; } Entry<?,?>[] newMap = new Entry<?,?>[newCapacity]; modCount++; threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1); table = newMap; for (int i = oldCapacity ; i-- > 0 ;) { //i下标对单向链表 for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) { Entry<K,V> e = old; old = old.next; //获取节点新下标 int index = (e.hash & 0x7FFFFFFF) % newCapacity; //链接起来 e.next = (Entry<K,V>)newMap[index]; newMap[index] = e; } }}
拉链发解决冲突图示:
Properties简介
Properties 继承于 Hashtable,可以作为一个map使用。其内部扩展了一些方法,只允许添加String的key-value,不过都是基于hashTable实现的。其使用最多的是作为配置工具类使用。
使用
properties作为map使用
/** * properties作为map使用 */@Testpublic void propertiesUseAsMap() { Properties prop = new Properties(); prop.put(1, "a"); prop.put(2, 2); prop.put("3", new Object()); System.out.println(prop.get(1)); System.out.println(prop.get(2)); System.out.println(prop.get("3"));}
其扩展方法,只能添加字符串的key-valu
@Testpublic void useAsProp() {Properties prop = new Properties();prop.setProperty("name","name");prop.setProperty("age","23");prop.setProperty("email","123@qq.com");System.out.println(prop);prop.forEach((key, value) -> {System.out.println("{key" + key + "," + "value" + value + "}");});}
有一些类自带properties属性
/*** 也可以自己写一个配置类*/@Testpublic void testMyCP() {Properties prop = MyPropClass.getProperties();
System.out.println(prop);prop.forEach((key, value) -> { System.out.println("{key" + key + "," + "value" + value + "}");});
}
class MyPropClass { private static Properties props; private static final String name; private static final String age; private static final String email;
static { props = new Properties(); name = "yyc"; age = "23"; email = "123@qq.com"; initProperties();}private static void initProperties() { props.setProperty("name", name); props.setProperty("age", age); props.setProperty("email", email);}public void setProperties(String key, String value) { props.setProperty(key, value);}public String getProperties(String key) { return props.getProperty(key);}public void setProperties(Properties properties) { MyPropClass.props = properties;}public static Properties getProperties() { return props;}
> 为了防止一些硬编码,可以使用配置文件的形式配置信息,一般用于数据库链接或一些配置的类信息。user.properties:```propertiesname=yycage=23hobby=ball
public synchronized V put(K key, V value) { // Make sure the value is not null //不允许插入null值 if (value == null) { throw new NullPointerException(); } // Makes sure the key is not already in the hashtable. Entry<?,?> tab[] = table; //这里并没有对key进行为空检测,是不合理的 int hash = key.hashCode(); //hash值与0x7FFFFFFF按位与,防止出现负数。 //与tab.length做取余操作,得到散列下标 int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entry<K,V> entry = (Entry<K,V>)tab[index]; //不为空 for(; entry != null ; entry = entry.next) { //出现重复则覆盖 if ((entry.hash == hash) && entry.key.equals(key)) { V old = entry.value; entry.value = value; return old; } } //出现冲突、或对应下标节点为空,插入元素 addEntry(hash, key, value, index); return null;}0
同时也可以将Properties作为文件输出
public synchronized V put(K key, V value) { // Make sure the value is not null //不允许插入null值 if (value == null) { throw new NullPointerException(); } // Makes sure the key is not already in the hashtable. Entry<?,?> tab[] = table; //这里并没有对key进行为空检测,是不合理的 int hash = key.hashCode(); //hash值与0x7FFFFFFF按位与,防止出现负数。 //与tab.length做取余操作,得到散列下标 int index = (hash & 0x7FFFFFFF) % tab.length; @SuppressWarnings("unchecked") Entry<K,V> entry = (Entry<K,V>)tab[index]; //不为空 for(; entry != null ; entry = entry.next) { //出现重复则覆盖 if ((entry.hash == hash) && entry.key.equals(key)) { V old = entry.value; entry.value = value; return old; } } //出现冲突、或对应下标节点为空,插入元素 addEntry(hash, key, value, index); return null;}1
原文:https://juejin.cn/post/7103534218403135525