zoukankan      html  css  js  c++  java
  • Map集合 总结

    (本人第一次写博客,部分内容有参照李刚老师的疯狂java系列图书,如有遗漏错误,请多指教,谢谢。)

    Java的集合类可分为Set、List、Map、Queue,其中Set、List、Queue都有共同的接口:Collection.

    所以Collection和Map是Java集合框架的根接口。Java集合实际上并不是真的把对象放入其中,集合中保存的只是对象的引用。

    这里首先讲Map,因为所有的Set底层都是由Map实现的,仔细观察API可以发现,Set集合继承体系中所有的接口、实现类的类名,对应的Map集合体系都有。

    如:Set-->Map

      EnumSet-->EnumMap

      SortedSet-->SortedMap

      TreeSet-->TreeMap

      NavigableSet-->NavigableMap

      HashSet-->HashMap

      LinkedHashSet-->LinkedHashMap

    所不同的是,Map集合体系中还包括IdetityHashMap、WeakHashMap、Hashtable、Properties实现类。

    Map集合用于保存具有映射关系的数据,其本质就是一个Object类型的动态数组,数组元素是Map接口的内部类Entry。

    内部类Entry封装了一个key-value对。

    key和value存在单向的一对一关系,通过指定的key总能找到唯一的确定的value.

    由于这个特征,就必须要求Key是不可重复的,符合Set的特征。事实上,Map中所有的key就组成了一个Set集合。

    而Value是可以重复的。只能通过key查询value,可以把value看作key的附庸。

    一、HashMap 和 Hashtable

    Hashtable较为古老,是HashMap的线程安全形式。其中封装了许多古老的方法,与HashMap主要有两点区别:

    1、HashMap线程不安全,Hashtable线程安全,所以HashMap性能更好。

      而实现线程安全用集合工具类Collections的静态方法synchronizedMap()包装一下,更简单、实用。

    2、Hashtable不允许使用null作为key和value,会引发NullPointerException异常,而HashMap允许。

    HashMap的底层是数组实现的,其数组元素为一个Entry链表(栈)。

    存储过程:根据元素的hashCode()返回值计算元素在数组中的存储索引。根据元素equals方法来计算元素在Entry链表中的存储位置。

      若有2个元素hashCode()返回值相同而equals为false,那么发生哈希冲突。新增加的Entry总放在Entry链表栈顶。

      HashMap底层数组默认大小为16,这种存储位置又叫做桶(bucket),默认负载极限为0.75,即当HashMap中填满3/4时,

      HashMap的容量将自动增加一倍。这个过程中,元素会重新分配,放入新的桶中。这个过程叫做rehashing.

     1 public V put(K key, V value) {
     2         if (key == null)
     3             return putForNullKey(value);
     4         int hash = hash(key.hashCode());
     5         int i = indexFor(hash, table.length);
     6         for (Entry<K,V> e = table[i]; e != null; e = e.next) {
     7             Object k;
     8             if (e.hash == hash && ((k = e.key) == key ||       key.equals(k)))     {
     9                 V oldValue = e.value;
    10                 e.value = value;
    11                 e.recordAccess(this);
    12                 return oldValue;
    13             }
    14         }
    15         modCount++;
    16         addEntry(hash, key, value, i);
    17         return null;
    18     }
    HashMap的put方法源代码
    1  static int indexFor(int h, int length) {
    2         return h & (length-1);
    3     }
    索引的计算方法

      可以看到:1、元素的hash值是根据key的hashCode计算的。

           2、索引是根据元素的hash值简易计算的。

    查询过程:与存储过程类似,先根据hashCode()返回值计算数组索引,若数组所在位置已经有元素,与栈中所有元素equals比较,若相同,则返回。

     1  public V get(Object key) {
     2         if (key == null)
     3             return getForNullKey();
     4         int hash = hash(key.hashCode());
     5         for (Entry<K,V> e = table[indexFor(hash, table.length)];
     6              e != null;
     7              e = e.next) {
     8             Object k;
     9             if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
    10                 return e.value;
    11         }
    12         return null;
    13     }
    HashMap的get方法

      所有参与计算hashCode()返回值的关键属性,都应用于作为equals()比较的标准。

      不应该给equals()和hashCode()方法的依赖属性提供注入方法,或者可以直接将其设置为final.

    创建过程:除了默认的构造方法,HashMap还提供了指定初始容量、负载因子的构造器。

     1 public HashMap(int initialCapacity, float loadFactor) {
     2         if (initialCapacity < 0)
     3             throw new IllegalArgumentException("Illegal initial capacity: " +
     4                                                initialCapacity);
     5         if (initialCapacity > MAXIMUM_CAPACITY)
     6             initialCapacity = MAXIMUM_CAPACITY;
     7         if (loadFactor <= 0 || Float.isNaN(loadFactor))
     8             throw new IllegalArgumentException("Illegal load factor: " + loadFactor);
     9         // Find a power of 2 >= initialCapacity
    10         int capacity = 1;
    11         while (capacity < initialCapacity)
    12             capacity <<= 1;
    13 
    14         this.loadFactor = loadFactor;
    15         threshold = (int)(capacity * loadFactor);
    16         table = new Entry[capacity];
    17         init();
    18     }
    HashMap构造方法源代码

      可以看到:1、初始容量最大为1<<30。不可以更大。

           2、初始容量必须为大于指定容量的最小的2的n次方值

    二、TreeMap

    TreeMap采用“红黑树”的排序二叉树来保存Map中的每个Entry。每个Entry为红黑树的一个节点。

    所有的Entry总是根据key按指定的规则保持有序。

    红黑树是一种自平衡二叉树,每个节点的值都大于或等于它的左子树中所有节点的值,都小于或等于它的右子树中所有节点的值。

    三、WeakHashMap、IdentityHashMap

    WeakHashMap每个key对象保存了实际对象的弱引用。

    IdentityHashMap的实现机制与HashMap类似,只有当key1==key2时,才认为key1与key2相等。它不保证任何key-value对之间的顺序,

    也不保证它们的顺序随时间的推移不变。

    四、LinkedHashMap、Properties

    LinkedHashMap是HashMap的子类,使用双向链表来维护entry的插入次序,迭代输出时,元素顺序与插入顺序一致。

    Properties也是HashMap的子类,把Map对象和属性文件关联起来,把Map对象的key和value与属性文件的属性名和属性值关联起来。

    五、EnumMap

    1、EnumMap所有key必须是枚举类的枚举值。

    2、在内部以数组实现,根据key自然排序。

    3、不允许使用null作为key,允许使用null作为value

    4、创建EnumMap时必须指定一个枚举类,将EnumMap与指定枚举类关联起来。

    5、EnumMap是性能最好的Map

    另:Map有一个values方法,返回一个集合对象,

      其实,这个Values集合对象并未盛装任何java对象,主要用来遍历map中的所有value值。

    /**
    *   ————————如果觉得本博文还行,别忘了推荐一下哦,谢谢!
    *   作者:钱书康
    *   欢迎转载,请保留此段声明。
    *   出处:http://www.cnblogs.com/zrtqsk/
    */
  • 相关阅读:
    卡尔曼滤波器的简单推导
    data_quick 进度
    面试
    MapServer+TileCache+Apache+Python24 构建KS数据服务器
    PYTHONPATH 可以跨版本 方便使用 (本文为windows方法)转~
    20130923
    20130909
    error C2504 类的多层继承 头文件包含
    int long 等基础类型在不同平台的大小
    mysql python image 图像存储读取
  • 原文地址:https://www.cnblogs.com/zrtqsk/p/3472258.html
Copyright © 2011-2022 走看看