【java源码】Hashtable详解及Properties

Hashtable

核心成员变量

  1. private transient Entry<?,?>[] table;,存放键值对
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) {
        ...
    }

    public boolean equals(Object o) {
        ...
    }

    public int hashCode() {
        return hash ^ Objects.hashCode(value);
    }

    public String toString() {
        return key.toString()+"="+value.toString();
    }
}

构造器

  1. public Hashtable(int initialCapacity, float loadFactor),主要构造器
  2. Hashtable(int initialCapacity),实质是调用1,默认loadFactor=0.75
  3. Hashtable(),实质是调用1,默认initialCapacity=11loadFactor=0.75
  4. Hashtable(Map<? extends K, ? extends V> t),实质是调用1,容量初始大小在原大小的两倍和11之间取大者,默认loadFactor=0.75

成员方法

  1. put(K key, V value),借助addEntry方法添加新结点
    细节:
    1. hash & 0x7FFFFFFF,保证hash值为正。不用abs函数的原因是,abs函数无法处理最小负数-2^31
    2. 采用%实现取余。这里不同于HashMap,没有tableSizeFor保证容器大小始终为2的幂。因此,只能采用%符合。
    3. value == nullkey.hashCode();导致Hashtable键与值均不能为null,否则均会抛出空指针异常

那么问题来了

  1. Hashtable为什么不采用HashMap的策略,始终保持容器为2的幂?
  2. Hashtable为什么没有树化机制?
  3. 为什么Hashtable继承的是Dictionary类以实现键值对,而HashMap通过继承AbstractMap类以实现键值对?
    这两个问题答案都是一样的。
    Dictionary类是过时类,Hashtable是1.0版本实现键值对的类。现在因为线程安全的特点而被继续使用。
    另外,Hashtable没有采用驼峰命名,也是因为驼峰命名是在1.2版本约定的。

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;
    @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. addEntry(int hash, K key, V value, int index),借助rehash实现扩容
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

  1. 每次扩容为原容量的两倍+1,(oldCapacity << 1) + 1
  2. 若新容量超过最大值则不再扩容
    注意,没有树化机制
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 ;) {
        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;
        }
    }
}

entrySet()、EntrySet、Enumerator

Enumerator等同于HashMap类的EntryIterator 。功能逻辑上与HashMap是一致的。
不同细节:

  1. 使用Enumerator统一实现HashMap类中xxxIterator的功能
  2. 构造Enumerator对象时传入type,如KEYSVALUESENTRIES用于控制迭代时返回键、值还是键值对。
public Set<Map.Entry<K,V>> entrySet() {
    if (entrySet==null)
        entrySet = Collections.synchronizedSet(new EntrySet(), this);
    return entrySet;
}
private class EntrySet extends AbstractSet<Map.Entry<K,V>> {
    public Iterator<Map.Entry<K,V>> iterator() {
        return getIterator(ENTRIES);
    }

    public boolean add(Map.Entry<K,V> o) {
        return super.add(o);
    }

    public boolean contains(Object o) {
        ...
    }

    public boolean remove(Object o) {
        ...
    }

    public int size() {
        return count;
    }

    public void clear() {
        Hashtable.this.clear();
    }
}
private class Enumerator<T> implements Enumeration<T>, Iterator<T> {
    Entry<?,?>[] table = Hashtable.this.table;
    int index = table.length;
    Entry<?,?> entry;
    Entry<?,?> lastReturned;
    int type;

    /**
     * Indicates whether this Enumerator is serving as an Iterator
     * or an Enumeration.  (true -> Iterator).
     */
    boolean iterator;

    /**
     * The modCount value that the iterator believes that the backing
     * Hashtable should have.  If this expectation is violated, the iterator
     * has detected concurrent modification.
     */
    protected int expectedModCount = modCount;

    Enumerator(int type, boolean iterator) {
        this.type = type;
        this.iterator = iterator;
    }

    public boolean hasMoreElements() {
        ...
    }

    @SuppressWarnings("unchecked")
    public T nextElement() {
        Entry<?,?> et = entry;
        int i = index;
        Entry<?,?>[] t = table;
        /* Use locals for faster loop iteration */
        while (et == null && i > 0) {
            et = t[--i];
        }
        entry = et;
        index = i;
        if (et != null) {
            Entry<?,?> e = lastReturned = entry;
            entry = e.next;
            return type == KEYS ? (T)e.key : (type == VALUES ? (T)e.value : (T)e);
        }
        throw new NoSuchElementException("Hashtable Enumerator");
    }

    // Iterator methods
    public boolean hasNext() {
        return hasMoreElements();
    }

    public T next() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        return nextElement();
    }

    public void remove() {
        ...
    }
}

Properties

继承了Hashtable类,主要是为IO流中读取、修改配置文件服务,配置文件常为.properties文件。这种设计体现了高内聚、低耦合的思想。
方法和成员变量较为简单,感兴趣的朋友可以看这位博主的博文。

https://blog.csdn.net/weixin_53972936/article/details/123899368

需要注意一点,Properties中存的键值对都必须是String类型。为此在Properties中通过独有的setPropertygetProperty方法进行了控制。如果利用父类的方法存入了非String类型的键值对,这种不安全的做法将导致调用不了store save方法。

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值