8-Map 集合

Map 集合

Map 用于保存具有映射关系–”key-value” 的数据。

HashMap、Hashtable 判断两个 key 相等的标准是:两个 key 的 hashCode 值相等,并且两个 key 通过 equals() 方法比较返回 true。

HashMap、Hashtable 判断两个 value 相等的标准是:两个 value 通过 equals() 方法比较返回 true 即可。

Mapkey 不允许重复,同一个 Map 对象的任何两个 key 通过 equals 方法比较总是返回 false。keyvalue 之间存在单向一对一关系,通过指定的 key ,总能找到唯一、确定的 value。把 Map 里所有的 key 通过 keySet() 方法返回 Map 里所有 key 组成的 Set 集合。

Map 非常类似于 List,其中 values 元素可以重复,只不过 Map 中的索引是使用 key 对象。如果需要从 Map 中取出元素 value,则需要提供该元素的 key 索引。

在 Java 源码中,是先实现了 Map,然后通过包装一个所有 values 都为 null 的 Map 就实现了 Set。

Map 中定义了如下常用方法:

Map 接口提供了大量的实现类,如 HashMapHashtableHashMap 的子类 LinkedHashMap,还有 SortedMap 子接口以及该接口的实现类 TreeMap,以及 WeakHashMapIdentityHashMap

Map 中包含了一个内部类 Entry,该内部类封装了一个 key-value 对,Entry 包含如下方法:

  • Object getKey():返回该 Entry 里包含的 key 值。
  • Object getValue():返回该 Entry 里包含的 value 值。
  • Object setValue(V value):设置该 Entry 里包含的 value 值,并返回新设置的 value 值。

Map 集合典型的用法就是成对地添加、删除 key-value 对,接下来即可判断该 Map 中是否包含指定 key,是否包含指定 value,也可以通过 Map 提供的 keySet() 方法获取所有 key 组成的集合,进而遍历 Map 中所有的 key-value 对。HashMap 重写了 toString() 方法,返回如下格式的字符串:{ key1=value1,key2=value2…. }

Java8 为 Map 新增的方法


Java8 改进的 HashMap 和 Hashtable 实现类

HashMapHashtable 之间的关系类似于 ArrayList 和 Vectory。但 Hashtable 过于古老,基本不用。它们的区别主要在 Hashtable 是一个线程安全的 Map 实现;而且 Hashtable 不允许使用 null 作为 key 和 value,但 HashMap 可以使用 null 作为 key 或 value(只允许存在一个 null)。

总结:在 hashMap 中放入(put)元素,有以下重要步骤:

  1. 计算 key 的 hash 值,算出元素在底层数组中的下标位置。
  2. 通过下标位置定位到底层数组里的元素(也有可能是链表也有可能是树)。
  3. 取到元素,判断放入元素的 key 是否 == 或 equals 当前位置的 key,成立则替换 value 值,返回旧值。
    4 如果是树,循环树中的节点,判断放入元素的 key 是否 == 或 equals 节点的 key,成立则替换树里的 value,并返回旧值,不成立就添加到树里。
  4. 否则就顺着元素的链表结构循环节点,判断放入元素的 key 是否 == 或 equals 节点的 key,成立则替换链表里 value,并返回旧值,找不到就添加到链表的最后。

精简一下,判断放入HashMap中的元素要不要替换当前节点的元素,key满足以下两个条件即可替换:

  1. hash 值相等。
  2. == 或 equals 的结果为 true。

由于 hash 算法依赖于对象本身的 hashCode 方法,所以对于 HashMap 里的元素来说,hashCode 方法与 equals 方法非常的重要,重写对象的 equals 方法一定要重写 hashCode 方法,不重写的话,放到 HashMap 中可能会得不到你想要的结果!本示例中放入的 key 是 String 类型的,String 这个类已经重写了 hashCode 方法。

LinkedHashMap 实现类

LinkedHashMap 使用双向链表来维护 key-value 的次序,该链表负责维护 Map 的迭代顺序,顺序与 key-value 对的插入顺序保持一致。迭代输出元素时,将会按添加 key-value 对的顺序输出。

使用 Properties 读写属性文件

Properties 类是 Hashtable 类的子类,该对象在处理属性文件时特别方便。它可以把 Map 对象和属性文件关联起来,从而可以把 Map 对象中的 key-value 对写入属性文件中,也可以把属性文件中的 “属性名=属性值” 加载到 Map 对象中。

Properties 相当于一个 key、value 都是 String 类型的 Map。

Properties 类提供了如下三个方法来修改 Properties 里的 key、value值:

  • String getProperty(String key):获取 Properties 中指定属性名对应的属性值,类似于 Map 的 get(Object key)方法。
  • String getProperty(String key,String defaultValue):该方法与前一个方法基本相似,如果不存在指定 key 时,可以指定默认值。
  • Object setProperty(String key,String value):设置属性值。
  • void load(InputStream inStream):从属性文件中加载 key-value 对,把加载到的 key-value 对追加到 Properties 里。
  • void store(OutputStream out,String comments):将 Properties 中的 key-value 对输出到指定的属性文件中

SortedMap 接口和 TreeMap 实现类

Map 接口也派生出一个 SortedMap 子接口,SortedMap 接口也有一个 TreeMap 实现类。

TreeMap 是一个红黑树数据结构,每个 key-value 对即作为红黑树的一个节点。TreeMap 存储 key-value 对(节点) 时,需要根据 key 对节点进行排序。TreeMap 可以保证所有的 key-value 对处于有序状态

TreeMap 也有两种排列方式:

  • 自然排序:TreeMap 的所有 key 必须实现 Comparable 接口,且所有的 key 应该是同一个类的对象。
  • 定制排序:创建 TreeMap 时,传入一个 Comparator 对象,该对象负责对 TreeMap 中的所有 key进行排序。

TreeMap 中判断两个 key 相等的标准是:两个 key 通过 compareTo() 返回 0。使用自定义类作为 TreeMap 的 key,则重写该类的 equals() 方法和 compareTo() 方法时应该保持一致的返回结果:两个 key 通过 equals() 方法返回 true 时,它们通过 compareTo() 方法比较应该也返回 0.


WeakHashMap 实现类

HashMap 的 key 保留了对实际对象的 强引用,意味着只要该 HashMap 对象不被销毁,该 HashMap 的所有 key 所引用的对象就不会被垃圾回收,HashMap 也不会自动删除这些 key 所对应的 key-value 对;

WeakHashMap 的 key 保留了对实际对象的弱引用,意味着如果 WeakHashMap 对象的 key 所引用的对象没有被其他强引用变量所引用,则这些 key 所引用的对象可能被垃圾回收,WeakHashMap 也可能自动删除这些 key 所对应的 key-value。

IdentityHashMap 实现类

IdentityHashMap 在处理两个 key 相等时比较独特:当且仅当两个 key 严格相等 (key1 == key2)时,IdentityHashMap 才认为两个 key 相等;对于普通的 HashMap 而言,只要 key1 和 key2 通过 equals() 方法比较返回 true,且它们的 hashCode 值相等即可。

EnumMap 实现类

EnumMap 是一个与枚举类一起使用的 Map 实现,EnumMap 中的所有 key 都必须是单个枚举类的枚举值。创建 EnumMap 时必须显式或隐式指定它对应的枚举类。

EnumMap 具有如下特征:

  • EnumMap 在内部以数组形式保存,所以这种实现形式非常紧凑、高效。
  • EnumMap 根据 key 的自然顺序(即枚举值在枚举类中的定义顺序)来维护 key-value 对的顺序。当程序通过 keySet()、entrySet()、value() 等方法遍历 EnumMap 时可以看到这种顺序。
  • EnumMap 不允许使用 null 作为 key,但允许使用 null 作为 value。

创建 EnumMap 时必须指定一个枚举类,从而将该 EnumMap 和 指定枚举类关联起来。

1
2
3
4
5
6
enum Season
{
SPRING、SUMMER、FALL、WINTER;
}

EnumMap enumMap=new EnumMap(Season.class);

HashSet 和 HashMap 性能分析