Map 接口的实现类 HashMap,Hashtable,TreeMap

1. HsahMap

1.1 概述,API文档介绍

  • 开始于JDK1.2
  • 基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
  • 此实现假定哈希函数将元素适当地分布在各桶之间,可为基本操作(get 和 put)提供稳定的性能。迭代 collection 视图所需的时间与 HashMap 实例的“容量”(桶的数量)及其大小(键-值映射关系数)成比例。所以,如果迭代性能很重要,则不要将初始容量设置得太高(或将加载因子设置得太低)。
  • HashMap 的实例有两个参数影响其性能:初始容量加载因子容量 是哈希表中桶的数量,初始容量只是哈希表在创建时的容量。加载因子 是哈希表在其容量自动增加之前可以达到多满的一种尺度。当哈希表中的条目数超出了加载因子与当前容量的乘积时,则要对该哈希表进行 rehash 操作(即重建内部数据结构),从而哈希表将具有大约两倍的桶数。
  • 通常,默认加载因子 (0.75) 在时间和空间成本上寻求一种折衷。加载因子过高虽然减少了空间开销,但同时也增加了查询成本(在大多数 HashMap 类的操作中,包括 get 和 put 操作,都反映了这一点)。在设置初始容量时应该考虑到映射中所需的条目数及其加载因子,以便最大限度地减少 rehash 操作次数。如果初始容量大于最大条目数除以加载因子,则不会发生 rehash 操作。
  • 如果很多映射关系要存储在 HashMap 实例中,则相对于按需执行自动的 rehash 操作以增大表的容量来说,使用足够大的初始容量创建它将使得映射关系能更有效地存储。

1.2 声明和构造方法

声明:public class HashMap<K,V>extends AbstractMap<K,V>implements Map<K,V>, Cloneable, Serializable

// HashMap 构造方法摘要 
HashMap() // 构造一个具有默认初始容量 (16) 和默认加载因子 (0.75) 的空 HashMap。 
HashMap(int initialCapacity) // 构造一个带指定初始容量和默认加载因子 (0.75) 的空 HashMap。 
HashMap(int initialCapacity, float loadFactor) // 构造一个带指定初始容量和加载因子的空 HashMap。 
HashMap(Map<? extends K,? extends V> m) // 构造一个映射关系与指定 Map 相同的新 HashMap。 

 1.3 HashMap 扩容原理

/**
 * Initializes or doubles table size.  If null, allocates in
 * accord with initial capacity target held in field threshold.
 * Otherwise, because we are using power-of-two expansion, the
 * elements from each bin must either stay at same index, or move
 * with a power of two offset in the new table.
 *
 * @return the table
 */
final Node<K,V>[] resize() {
    Node<K,V>[] oldTab = table;
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    int oldThr = threshold;
    int newCap, newThr = 0;
    if (oldCap > 0) {
        if (oldCap >= MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return oldTab;
        }	// 扩容至原来的 2 倍
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                 oldCap >= DEFAULT_INITIAL_CAPACITY)
            newThr = oldThr << 1; // double threshold
    }
    else if (oldThr > 0) // initial capacity was placed in threshold
        newCap = oldThr;
    else {               // zero initial threshold signifies using defaults
        newCap = DEFAULT_INITIAL_CAPACITY;
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }
    if (newThr == 0) {
        float ft = (float)newCap * loadFactor;
        newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                  (int)ft : Integer.MAX_VALUE);
    }
    threshold = newThr;
    @SuppressWarnings({"rawtypes","unchecked"})
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    table = newTab;
    if (oldTab != null) {    // 将 map 里面的元素复制到新的 map 里面
        for (int j = 0; j < oldCap; ++j) {
            Node<K,V> e;
            if ((e = oldTab[j]) != null) {
                oldTab[j] = null;
                if (e.next == null)
                    newTab[e.hash & (newCap - 1)] = e;
                else if (e instanceof TreeNode)
                    ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                else { // preserve order
                    Node<K,V> loHead = null, loTail = null;
                    Node<K,V> hiHead = null, hiTail = null;
                    Node<K,V> next;
                    do {
                        next = e.next;
                        if ((e.hash & oldCap) == 0) {
                            if (loTail == null)
                                loHead = e;
                            else
                                loTail.next = e;
                            loTail = e;
                        }
                        else {
                            if (hiTail == null)
                                hiHead = e;
                            else
                                hiTail.next = e;
                            hiTail = e;
                        }
                    } while ((e = next) != null);
                    if (loTail != null) {
                        loTail.next = null;
                        newTab[j] = loHead;
                    }
                    if (hiTail != null) {
                        hiTail.next = null;
                        newTab[j + oldCap] = hiHead;
                    }
                }
            }
        }
    }
    return newTab;
}

 

2. Hashtable

2.1 概述,API文档介绍

  • 开始于JDK1.0
  • 此类实现一个哈希表,该哈希表将键映射到相应的值。任何非 null 对象都可以用作键或值。
  • 为了成功地在哈希表中存储和获取对象,用作键的对象必须实现 hashCode 方法和 equals 方法。
  • Hashtable 的实例有两个参数影响其性能:初始容量 和加载因子。容量 是哈希表中桶 的数量,初始容量 就是哈希表创建时的容量。注意,哈希表的状态为 open:在发生“哈希冲突”的情况下,单个桶会存储多个条目,这些条目必须按顺序搜索。加载因子 是对哈希表在其容量自动增加之前可以达到多满的一个尺度。初始容量和加载因子这两个参数只是对该实现的提示。关于何时以及是否调用 rehash 方法的具体细节则依赖于该实现。
  • 通常,默认加载因子(0.75)在时间和空间成本上寻求一种折衷。
  • 初始容量主要控制空间消耗与执行 rehash 操作所需要的时间损耗之间的平衡。
  • 如果很多条目要存储在一个 Hashtable 中,那么与根据需要执行自动 rehashing 操作来增大表的容量的做法相比,使用足够大的初始容量创建哈希表或许可以更有效地插入条目。

2.2 声明和构造方法

声明:public class Hashtable<K,V>extends Dictionary<K,V>implements Map<K,V>, Cloneable, Serializable

// Hashtable 构造方法摘要 
Hashtable() // 用默认的初始容量 (11) 和加载因子 (0.75) 构造一个新的空哈希表。 
Hashtable(int initialCapacity) // 用指定初始容量和默认的加载因子 (0.75) 构造一个新的空哈希表。 
Hashtable(int initialCapacity, float loadFactor) // 用指定初始容量和指定加载因子构造一个新的空哈希表。 
Hashtable(Map<? extends K,? extends V> t) // 构造一个与给定的 Map 具有相同映射关系的新哈希表。 

2.3 Hashtable中的特殊的迭代方式 -- 枚举

定义:public interface Enumeration<E>

枚举是从JDK1.0版本出现的一种迭代方式,实现 Enumeration 接口的对象,它生成一系列元素,一次生成一个。连续调用 nextElement 方法将返回一系列的连续元素。

这些方法主要通过向量的元素、哈希表的键以及哈希表中的值进行枚举。枚举也用于将输入流指定到 SequenceInputStream 中。

与枚举作用相同的是迭代器,public interface Iterator<E>。始于JDK1.2版本。

迭代器相比于枚举:1. 简化了书写;2. 在迭代期间可以移除Collection中的元素。

public static void main(String[] args) {
	Hashtable<String, String> hashtable = new Hashtable<>();
	hashtable.put("JAVA01", "Java基础课程");
	hashtable.put("JAVA02", "Java高级课程");
	hashtable.put("JAVA03", "JavaWEB课程");
	hashtable.put("JAVA04", "JavaEE课程");
	
	Enumeration<String> keys = hashtable.keys();
	while (keys.hasMoreElements()) {
		String key = keys.nextElement();
		System.out.println(key + ":::" +hashtable.get(key));
	} 
}

2.4 Hashtable 扩容原理

/**
 * Increases the capacity of and internally reorganizes this
 * hashtable, in order to accommodate and access its entries more
 * efficiently.  This method is called automatically when the
 * number of keys in the hashtable exceeds this hashtable's capacity
 * and load factor.
 */
@SuppressWarnings("unchecked")
protected void rehash() {
    int oldCapacity = table.length;
    Entry<?,?>[] oldMap = table;

    // overflow-conscious code
    int newCapacity = (oldCapacity << 1) + 1;    // 将 容量扩容至:old * 2 + 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 ;) {    // 将 map 中的值复制到新 map 里面
        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;
        }
    }
}

3. TreeMap

3.1 概述,API文档介绍

  • 开始于JDK1.2
  • 基于红黑树(Red-Black tree)的 NavigableMap 实现。该映射根据其键的自然顺序进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。 
  • 此实现为 containsKey、get、put 和 remove 操作提供受保证的 log(n) 时间开销。这些算法是 Cormen、Leiserson 和 Rivest 的 Introduction to Algorithms 中的算法的改编。 

3.2 声明和构造函数

声明:public class TreeMap<K,V>extends AbstractMap<K,V>implements NavigableMap<K,V>, Cloneable, Serializable

// TreeMap 构造方法摘要 
TreeMap() // 使用键的自然顺序构造一个新的、空的树映射。 
TreeMap(Comparator<? super K> comparator) // 构造一个新的、空的树映射,该映射根据给定比较器进行排序。 
TreeMap(Map<? extends K,? extends V> m) // 构造一个与给定映射具有相同映射关系的新的树映射,该映射根据其键的自然顺序 进行排序。 
TreeMap(SortedMap<K,? extends V> m) // 与指定有序映射具有相同映射关系和相同排序顺序的新的树映射。 

3.3 TreeMap 里面一些专有方法

这些方法大都是从 Navigable 接口和 SortedMap接口中继承过来的,其中 Navigable 接口是 SortedMap 接口的拓展。具有了针对给定搜索目标返回最接近匹配项的导航方法。

K firstKey() // 返回此映射中当前第一个(最低)键。 
K lastKey() // 返回映射中当前最后一个(最高)键。 
K ceilingKey(K key) // 返回大于等于给定键的最小键;如果不存在这样的键,则返回 null。 
K floorKey(K key) // 返回小于等于给定键的最大键;如果不存在这样的键,则返回 null。 
K higherKey(K key) // 返回严格大于给定键的最小键;如果不存在这样的键,则返回 null。 
K lowerKey(K key) // 返回严格小于给定键的最大键;如果不存在这样的键,则返回 null。 

Map.Entry<K,V> firstEntry() // 返回一个与此映射中的最小键关联的键-值映射关系;如果映射为空,则返回 null。 
Map.Entry<K,V> lastEntry() // 返回与此映射中的最大键关联的键-值映射关系;如果映射为空,则返回 null。 
Map.Entry<K,V> ceilingEntry(K key) // 返回一个键-值映射关系,它与大于等于给定键的最小键关联;如果不存在这样的键,则返回 null。 
Map.Entry<K,V> floorEntry(K key) // 返回一个键-值映射关系,它与小于等于给定键的最大键关联;如果不存在这样的键,则返回 null。 
Map.Entry<K,V> higherEntry(K key) // 返回一个键-值映射关系,它与严格大于给定键的最小键关联;如果不存在这样的键,则返回 null。 
Map.Entry<K,V> lowerEntry(K key) // 返回一个键-值映射关系,它与严格小于给定键的最大键关联;如果不存在这样的键,则返回 null。 

Map.Entry<K,V> pollFirstEntry() // 移除并返回与此映射中的最小键关联的键-值映射关系;如果映射为空,则返回 null。 
Map.Entry<K,V> pollLastEntry() // 移除并返回与此映射中的最大键关联的键-值映射关系;如果映射为空,则返回 null。 

SortedMap<K,V> headMap(K toKey) // 返回此映射的部分视图,其键值严格小于 toKey。 
SortedMap<K,V> subMap(K fromKey, K toKey) // 返回此映射的部分视图,其键值的范围从 fromKey(包括)到 toKey(不包括)。 
SortedMap<K,V> tailMap(K fromKey) // 返回此映射的部分视图,其键大于等于 fromKey。 

NavigableSet<K> descendingKeySet() // 返回此映射中所包含键的逆序 NavigableSet 视图。 
NavigableSet<K> navigableKeySet() // 返回此映射中所包含键的 NavigableSet 视图。 
NavigableMap<K,V> descendingMap() // 返回此映射中所包含映射关系的逆序视图。 
NavigableMap<K,V> subMap(K fromKey, boolean fromInclusive, K toKey, boolean toInclusive) // 返回此映射的部分视图,其键的范围从 fromKey 到 toKey。 
NavigableMap<K,V> headMap(K toKey, boolean inclusive) // 返回此映射的部分视图,其键小于(或等于,如果 inclusive 为 true)toKey。 
NavigableMap<K,V> tailMap(K fromKey, boolean inclusive) // 返回此映射的部分视图,其键大于(或等于,如果 inclusive 为 true)fromKey。

4. 分析

Map<K,V>:将键映射到值的对象。一个映射不能包含重复的键;每个键最多只能映射到一个值。 
          此接口取代 Dictionary 类,后者完全是一个抽象类,而不是一个接口。 
	|--Hashtable
		1. 出现版本:JDK1.0
		2. 此实现是同步的
		3. 默认构建空间大小为 11,加载因子 0.75
		4. 容量不足的时候,扩容至原来的 2 倍加 1
		5. 不支持null key 和 null value, 会报出 NullPointException
		6. 支持的遍历方式: 迭代器,枚举
	|--HashMap
		1. 出现版本:JDK1.2
		2. 此实现不是同步的
		3. 默认构建空间大小为 16,加载因子 0.75
		4. 容量不足的时候,扩容至原来的 2 倍
		5. 支持null key 和 null value
		6. 支持的遍历方式: 迭代器
	|--TreeMap
		1. 出现版本:JDK1.2
		2. 此实现不是同步的
		3. 底层实现:二叉树(红黑树)

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值