Java集合 Map 集合 与 操作集合的工具类- Collections 的详细说明(javamap集合详解)

  本篇文章为你整理了Java集合 Map 集合 与 操作集合的工具类: Collections 的详细说明(javamap集合详解)的详细内容,包含有java中的集合分为collection和map两大类 javamap集合详解 java中map集合的用法 java集合list set map Java集合 Map 集合 与 操作集合的工具类: Collections 的详细说明,希望能帮助你了解 Java集合 Map 集合 与 操作集合的工具类: Collections 的详细说明。

  TheMagicalRainbowSea

  一个人的资金一定是与他(她)的能力相匹配的,无一例外。

  掘金: [ https://juejin.cn/user/752533564566951 ]

  腾讯云: [ https://cloud.tencent.com/developer/user/10317357 ]

  CSDN: [ https://blog.csdn.net/weixin_61635597?spm=1000.2115.3001.5343 ]

  
Map 接口与 Collection 并列存在的,用于保存具有映射关系的数据:key-value 被称为 键值对 。Java集合可分为 Collection 和 Map 两种体系。Map 中的 key 和 value 都可以是任何引用类型的数据。Map 中的 key 用 Set 集合存储的,不允许重复。即同一个 Map 对象所对应的类,必须重写hashCode() 和 equals() 方法。但是其中的 value 值是可以存储重复的数据的。而 value 值则是被 Collection 接口集合存储的。

  
Map 接口与 Collection 并列存在的,用于保存具有映射关系的数据:key-value 被称为 键值对 。

  
key 和 value 都是引用数据类型,都是存储对象的内存地址的。不是基本数据类型。

  其中 key 起到主导地位,value 是 key 的一个附属品。

  
Map 中的 key 用 Set 集合存储的,不允许重复。即同一个 Map 对象所对应的类,必须重写hashCode() 和 equals() 方法。但是其中的 value 值是可以存储重复的数据的。而 value 值则是被 Collection 接口集合存储的。

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

  
HashMap 作为Map的主要实现类,线程不安全的,效率高,可以存储 null 的key 和 value。HashMap是 Map 接口使用频率最高的实现类

  LinkedHashMap 保证再遍历Map元素时,可以按照添加的顺序实现遍历,原因: 在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。对于频繁的遍历操作,此类执行效率高于HashMap

  TreeMap 保证按照添加的 key-value键值对进行排序,实现排序遍历.此时考虑key的自然排序或定制排序,底层使用红黑树:

  Hashtalbe 作为古老的实现类,线程安全的,效率低,不可以存储 null

  Properties 主要用于配置文件的读取。

  
 

  2. Map接口:常用方法

  添加、删除、修改操作:

  put(K key, V value) : 将指定的 key 和 value 值添加/修改到该集合当中。

  

V put(K key,V value); // 将指定的 key 和 value 值添加/修改到该集合当中。

 

  

 

  putAll(Map m) : 将 m 中所有的key-value 值存放到当前 对象集合当中。

  

void putAll(Map ? extends K,? extends V // 将m中的所有key-value对存放到当前map集合当中

 

  

 

  remove(Object key) : 移除指定key的key-value对,并返回value。

  

V remove(Object key); // 移除指定key的key-value对,并返回value

 

  

 

  clear() : 清空当前map中的所有数据。

  

void clear(); // 清空当前map中的所有数据

 

  

 

  size() : 返回此集合中存储的元素数据(键值对)的数量。

  

int size(); // 返回此集合中存储的元素数据(键值对)的数量。

 

  

 

  举例:

  

import java.util.HashMap;

 

  import java.util.Map;

  public class MapTest {

   public static void main(String[] args) {

   // Map 接口 , HashMap实现类,多态, String,Integer 泛型

   Map String,Integer map = new HashMap String,Integer

   // 添加元素数据:

   map.put("zhangsan",66);

   map.put("lisi",89);

   map.put("wangwu",97);

   map.put("lihua",99);

   System.out.println(map);

   int size = map.size(); // 返回该集合中存储的键值对的数量。

   System.out.println(size);

   System.out.println("*********************");

   Integer zhangsan = map.remove("zhangsan"); // 移除key = zhangsan的元素数据,并返回该移除的value值。

   System.out.println(zhangsan);

   System.out.println(map);

   map.clear(); // 清空该Map 集合当中的存储的元素数据

   System.out.println(map.size());

  
 

 

  举例

  

import java.util.HashMap;

 

  import java.util.Map;

  public class MapTest {

   public static void main(String[] args) {

   // Map 接口 , HashMap实现类,多态, String,Integer 泛型

   Map String,Integer map = new HashMap String,Integer

   // 添加元素数据:

   map.put("zhangsan",66);

   map.put("lisi",89);

   Map String,Integer map2 = new HashMap String,Integer

   map2.put("wangwu",97);

   map2.put("lihua",99);

   map.putAll(map2); // 将 map2 集合中所有的key-value键值对添加到此 map集合当中去

   // 注意:两者集合存储的元素数据类型必须是一致的才可以添加成功。

   System.out.println(map);

  

 

  元素查询的操作:

  get(Object key) : 获取指定key对应的value。

  

V get(Object key); // 获取指定key对应的value

 

  

 

  containsKey(Object key) : 判断该集合当中是否包含指定的 key值。

  

boolean containsKey(Object key); // 判断该集合当中是否包含指定的 key 值。

 

  

 

  containsValue(Object key) : 判断该集合当中是否包含指定的 value 值。

  

boolean containsValue(Object value); // 判断判断该集合当中是否包含指定的 value 值。

 

  

 

  isEmpty() : 判断此集合是否为 空,是返回 true,不是返回 false。

  

boolean isEmpty(); // 判断此集合是否为 空,是返回 true,不是返回 false;

 

  

 

  equals(Object o) : 判断当前map和参数对象 o 是否相等。

  

boolean equals(Object o); // 判断当前map和参数对象 o 是否相等

 

  

 

  举例:

  

import java.util.HashMap;

 

  import java.util.Map;

  public class MapTest {

   public static void main(String[] args) {

   // Map 接口 , HashMap实现类,多态, String,Integer 泛型

   Map String,Integer map = new HashMap String,Integer

   // 添加元素数据:

   map.put("zhangsan",66);

   map.put("lisi",89);

   map.put("wangwu",97);

   map.put("lihua",99);

   System.out.println(map.get("zhangsan")); // 获取到对应 key上的 value值

   System.out.println(map.containsKey("zhangsan")); // 判断该集合当中是否存在 key = zhangsan的键值对

   System.out.println(map.containsValue(99)); // 判断该集合当中是否存在 value = 99的键值对

   System.out.println(map.isEmpty()); // 判断该集合是否为空

   System.out.println(map.equals(map)); // 判断当前map和参数对象 o 是否相等

  

 

  元视图操作的方法:

  keySet() : 返回所有key构成的Set集合。从该方法中可以看出 Map 接口下的集合中的 key 值是存储在 Set 接口集合当中的。

  

Set K keySet(); // 返回所有key构成的Set集合

 

  

 

  values() : 返回所有value构成的Collection集合。从该方法中可以看出 Map 接口下的集合中的 value 值是存储在 Collection 接口集合当中的。

  

Collection V values(); // 返回所有value构成的Collection集合

 

  

 

  举例:

  

import java.util.Collection;

 

  import java.util.HashMap;

  import java.util.Iterator;

  import java.util.Map;

  import java.util.Set;

  public class MapTest {

   public static void main(String[] args) {

   // Map 接口 , HashMap实现类,多态, String,Integer 泛型

   Map String,Integer map = new HashMap String,Integer

   // 添加元素数据:

   map.put("zhangsan",66);

   map.put("lisi",89);

   map.put("wangwu",97);

   map.put("lihua",99);

   Set String keys = map.keySet(); // 返回此集合当中所有的 key 值存储到 Set 集合当中

   for (String s : keys) {

   System.out.println(s);

   System.out.println("****************");

   Collection Integer values = map.values(); // 返回此集合当中所有的 value 值存储到 Collection 集合当中

   // Collection 接口集合可以使用迭代器

   Iterator Integer iterator = values.iterator();

   while(iterator.hasNext()) {

   System.out.println(iterator.next());

  

 

  entrySet() : 返回该集合当中的所有 key-value键值对,并存储到 Set 集合当中后,再返回一个 Set 集合对象(该集合存储了所有的key-value) 值。

  

Set Map.Entry K,V entrySet(); // 返回所有key-value对构成的Set集合

 

  

 

  其中的 Map.Entry 表示的是一个接口,也可以理解为是一个类。

  

* Set Map.Entry K,V entrySet() 将 Map集合转换成 Set集合

 

  * 假设现在有一个 Map集合 ,如下所示:

  * map1 集合对象

  * key value

  * 1 zhangsan

  * 2 lisi

  * 3 wangwu

  * 4 zhaoliu

  * Set set = mop1.entrySet();

  * set 集合对象

  * 1=zhangsan

  * 2=lisi

  * 3=wangwu

  * 4=zhaoliu

  

 

  Map.Entry K,V 的图示:

  举例:

  

import java.util.Collection;

 

  import java.util.HashMap;

  import java.util.Iterator;

  import java.util.Map;

  import java.util.Set;

  public class MapTest {

   public static void main(String[] args) {

   // Map 接口 , HashMap实现类,多态, String,Integer 泛型

   Map String,Integer map = new HashMap String,Integer

   // 添加元素数据:

   map.put("zhangsan",66);

   map.put("lisi",89);

   map.put("wangwu",97);

   map.put("lihua",99);

   Set Map.Entry String, Integer entries = map.entrySet();

   Iterator Map.Entry String, Integer iterator = entries.iterator();

   while(iterator.hasNext()) {

   Map.Entry String, Integer entry = iterator.next();

   // getKey()获取 key 值,getValue()获取value值

   System.out.println(entry.getKey() + "--- " + entry.getValue());

  

 

  3. Map实现类之一:HashMap

  
 

  
HashMap 允许存储 null 值,key 可以为 null ,但仅仅只能有一个,因为不可重复,value 可以为 null 。无序

  
HashMap 中所有的 key 构成的集合是存储在 Set 当中的,无序的,不可重复的,所以:key 所在类和 Set 集合是一样的必须重写 euqlas() 和 hashCode() 方法。其中 Java当中的包装类和String 类都重写了 equals() 和 hashCode()方法。基本上只有我们自定的类需要重写。

  
HashMap 判断两个 key 相等的标准是 : 两个key 通过 equals() 方法返回 true , hashCode 值也相等。

  
数组:在查询方面效率很高,随机增删方面很低。

  链表:在随机增删方面效率较高,在查询方面效率低。

  而哈希表:将以上两种数据结构融合在一起,充分发挥它们各自的优点。

  
对于 HashMap 中的方法基本上都是继承了对应的 Map 接口的方法,上面已经说明了,这里就不多介绍了。

  
举例:

  如下是 Set 中的 Key 存储自定义类 Person5 ,其中并没有重写Object 中的 equals() 方法和 hashCode()方法。会出现 Key 存储到重复的数据。

  

package blogs.blogs7;

 

  import java.util.HashMap;

  import java.util.Map;

  import java.util.Set;

  public class HashMapTest {

   public static void main(String[] args) {

   HashMap Person5,Integer hashMap = new HashMap Person5, Integer

   hashMap.put(new Person5("Tom",19),1);

   hashMap.put(new Person5("Tom",19),1);

   hashMap.put(new Person5("Tom",19),1);

   hashMap.put(new Person5("zhangsan",23),4);

   hashMap.put(new Person5("lihua",20),5);

   // 遍历HashMap 集合

   Set Map.Entry Person5,Integer entries = hashMap.entrySet();

   for (Map.Entry Person5,Integer entry : entries) {

   System.out.println(entry);

  
 

 

  修改: 重写其中的 Key 值的 Set 集合中存储的 类中的 equals() 和 hashCode() 方法。

  

package blogs.blogs7;

 

  import java.util.HashMap;

  import java.util.Map;

  import java.util.Objects;

  import java.util.Set;

  public class HashMapTest {

   public static void main(String[] args) {

   HashMap Person5,Integer hashMap = new HashMap Person5, Integer

   hashMap.put(new Person5("Tom",19),1);

   hashMap.put(new Person5("Tom",19),1);

   hashMap.put(new Person5("Tom",19),1);

   hashMap.put(new Person5("zhangsan",23),4);

   hashMap.put(new Person5("lihua",20),5);

   // 遍历HashMap 集合

   Set Map.Entry Person5,Integer entries = hashMap.entrySet();

   for (Map.Entry Person5,Integer entry : entries) {

   System.out.println(entry);

  
 

 

  HashMap中的 Key值可以存储添加 null 值,但是仅仅只能添加一个 null ,因为 Key 中的数据存储在 Set集合当中的,不可重复,而 Value 值也可以存储 null值,而且可以存储多个 null 值,因为 Value 值数据底层是存储在Collection集合当中的。

  举例:

  

import java.util.HashMap;

 

  import java.util.Map;

  import java.util.Objects;

  import java.util.Set;

  public class HashMapTest {

   public static void main(String[] args) {

   HashMap String,Integer hashMap = new HashMap String, Integer

   hashMap.put(null,null);

   hashMap.put(null,null);

   hashMap.put(null,null);

   hashMap.put("1",null);

   hashMap.put("2",null);

   hashMap.put("3",null);

   // 遍历HashMap 集合

   Set Map.Entry String,Integer entries = hashMap.entrySet();

   for (Map.Entry String,Integer entry : entries) {

   System.out.println(entry);

  

 

  常用方法总结:

  添加: put(Object key,Object value)

  删除: remove(object key)

  **修改: **put(Object key,Object value)

  查询: get(Object key)

  长度: size();

  遍历: keySet()/values()/entrySet()

  3.1 HashMap的存储结构

  JDK 7及以前版本:HashMap是数组+链表结构(即为链地址法)

  JDK 8版本发布以后:HashMap是数组+链表+红黑树实现

  如下是 JDK8 的HashMap 结构图

  3.2 HashMap源码中的重要常量

  

/**

 

   * The default initial capacity - MUST be a power of two.

  static final int DEFAULT_INITIAL_CAPACITY = 1 4; // aka 16 HashMap的默认容量是 16

  -----------------------------------------------------------------------------------

  * The maximum capacity, used if a higher value is implicitly specified

  * by either of the constructors with arguments.

  * MUST be a power of two = 1 30.

  static final int MAXIMUM_CAPACITY = 1 30; // HashMap的最大支持容量,2^30

  -----------------------------------------------------------------------------------

  * The load factor used when none specified in constructor.

  static final float DEFAULT_LOAD_FACTOR = 0.75f; // HashMap的默认加载因子

  -----------------------------------------------------------------------------------

  * The bin count threshold for using a tree rather than list for a

  * bin. Bins are converted to trees when adding an element to a

  * bin with at least this many nodes. The value must be greater

  * than 2 and should be at least 8 to mesh with assumptions in

  * tree removal about conversion back to plain bins upon

  * shrinkage.

  static final int TREEIFY_THRESHOLD = 8; // Bucket中链表长度大于该默认值,转化为红黑树

  -----------------------------------------------------------------------------------

  * The bin count threshold for untreeifying a (split) bin during a

  * resize operation. Should be less than TREEIFY_THRESHOLD, and at

  * most 6 to mesh with shrinkage detection under removal.

  static final int UNTREEIFY_THRESHOLD = 6; // Bucket中红黑树存储的Node小于该默认值,转化为链表

  -----------------------------------------------------------------------------------

  * The smallest table capacity for which bins may be treeified.

  * (Otherwise the table is resized if too many nodes in a bin.)

  * Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts

  * between resizing and treeification thresholds.

  static final int MIN_TREEIFY_CAPACITY = 64; // 桶中的Node被树化时最小的hash表容量。(当桶中Node的数量大到需要变红黑树时,若hash表容量小于MIN_TREEIFY_CAPACITY时,此时应执行resize扩容操作这个MIN_TREEIFY_CAPACITY的值至少是TREEIFY_THRESHOLD的4倍。)

  -----------------------------------------------------------------------------------

  * The table, initialized on first use, and resized as

  * necessary. When allocated, length is always a power of two.

  * (We also tolerate length zero in some operations to allow

  * bootstrapping mechanics that are currently not needed.)

  transient Node K,V [] table; // 存储元素的数组,总是2的n次幂

  -----------------------------------------------------------------------------------

  * Holds cached entrySet(). Note that AbstractMap fields are used

  * for keySet() and values().

  transient Set Map.Entry K,V entrySet; // 存储具体元素的集合

  -----------------------------------------------------------------------------------

  * The number of key-value mappings contained in this map.

  transient int size; // HashMap中实际存储的键值对的数量

  -----------------------------------------------------------------------------------

  * The number of times this HashMap has been structurally modified

  * Structural modifications are those that change the number of mappings in

  * the HashMap or otherwise modify its internal structure (e.g.,

  * rehash). This field is used to make iterators on Collection-views of

  * the HashMap fail-fast. (See ConcurrentModificationException).

  transient int modCount; // HashMap扩容和结构改变的次数。

  -----------------------------------------------------------------------------------

  * The next size value at which to resize (capacity * load factor).

  * @serial

  // (The javadoc description is true upon serialization.

  // Additionally, if the table array has not been allocated, this

  // field holds the initial array capacity, or zero signifying

  // DEFAULT_INITIAL_CAPACITY.)

  int threshold; // 扩容的临界值,=容量 * 填充因子

  -----------------------------------------------------------------------------------

  * The load factor for the hash table.

  * @serial

  final float loadFactor; // 填充因子

  

 

  3.3 HashMap的存储结构:JDK 1.8之前 / JDK 1.8之后

  3.3.1 JDk 1.8 之前

  
HashMap 内部存储结构其实是 数组 + 链表的结合。当实例化一个 new HashMap() 时,实际上会创建一个长度为 Capacity 的 Entry 数组。这个长度在 哈希表中称为 容量(Capacity) ,在这个数组中可以存放元素的位置,我们称之为 ”桶“ (bucket) ,每个 bucket 都有自己的索引,系统可以根据索引快速的查找 bucket 中的元素。

  
每个bucket 中存储一个元素,即 一个 Entry 对象内部类 ,但每一个 Entry 对象可以带 一个引用变量,用于指向下一个元素,因此,在一个桶 (bucket) 中,就有可能生成一个 Entry 链。而且新添加的元素作为链表的 head 。

  
 

  3.3.2 JDk 1.8 及之后

  JDK8: HashMap 的内部存储结构其实是:数组+链表+树 的结合。当实例化一个 new HashMap 时,会初始化 initilCapacity 和 loadFactor ,在 put() 第一对映射关系(键值对)添加时,系统会创建一个长度为 initilCapacity 的 Node 数组 ,这个长度在哈希表中被称为 ”容量" (Capacity),在这个数组中可以存放元素的位置,我们称之为 “桶”(bucket) ,每个 bucket 都有自己的索引,系统可以根据索引快速的查找 bucket 中的元素。

  每个bucket 中存储一个元素数据,既 一个 Node 对象,但每一个 Node 对象可以带一个引用变量 next ,用于指向下一个元素,因此,在一个桶中,就有可能生成一个 Node 链表。也可能是一个一个TreeNode 对象,每一个TreeNode 对象可以有两个叶子节点 left 和 right ,因此,在一个桶中,就有可能生成一个 TreeNode 树。而新添加的元素作为链表的 last ,或树的叶子节点。

  JDK1.8 源码分析:

  3.3.3 JDK8 HashMap 集合添加元素的过程

  向 HashMap 集合中添加 put(key1,value1) 键值对, 首先调用 元素 key1 所在类的 hashCode() 方法,来得到该 key1对象的 hashCode(哈希) 值。

  然后再根据得到的 hashCode (哈希)值,通过某种散列函数 计算除该对象在 HashSet 集合中底层Node[] 数组的存储位置(即为:索引下标位置),(这个散列函数会与底层数组的长度相计算得到在数组中的下标,并且这种散列函数计算还尽可能保证能均匀存储元素,越是散列分布,该散列函数设计的越好)。

  
如果没有其他元素数据存储,则 元素 key1-value1添加到该位置上。 —— 情况1

  如果有其它元素数据存储(或以链表形式存储的多个元素) : 则比较key1和已经存在的一个或多个数据的哈希值):

  
如果 key1的hashCode() 哈希值与已经存在的数据的哈希值都 不相等, 则元素 key1-value1添加到该数组链表上。—— 情况2

  
如果 key1 的hashCode() 哈希值 与 已经存在的数据的哈希值都 相等, 则调用 key1 元素所在类的 equals() 方法,, 判断比较所存储的内容是否和集合中存储的相等。

  如果 不相等 也就是 equals() 方法,返回 false ,则此时 key1-value1添加成功。—— 情况3

  如果 相等 也就是 equals()方法,返回 true,不添加,替换掉其中存放的 value 值为 value1 ,因为 key1 是唯一的不可重复的,但是其 对应的 value 值是可以重复的。

  
对应上述 添加成功的 情况2 和 情况3 而言,关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储

  
 

  如下是查找图示:

  假设将所有的hashCode()方法返回设定为不一样的值,可以吗?,有什么问题:

  不行,因为这样的话,就导致 HashMap 集合底层的哈希表就成为了一维数组了,没有链表的概念了,更没有哈希表的概念了。

  假设将所有的hashCode()方法返回设返回值固定为某个值,可以吗?,有什么问题:

  答:不可以,设将所有的hashCode()方法,返回值固定为某个值,那么会导致底层哈希表变成了纯单向链表。这种情况下我们称为:散列分别不均匀。

  什么时散列分布不均匀

  假设我们有 100 个元素,10个单向链表,那么每个单向链表上有10个节点,这是最好的,是散列分布均匀的

  3.3.4 JDK8 HashMap 进行 "扩容"和 "树形化"

  扩容

  当 put(Key1,value1) 添加键值对个数超过 数组大小(数组总大小 length ,不是数组中实际存放的键值对个数 size),时,就会进行数组扩容。loadFactor 的默认值:DEFAULT_LOAD_FACTOR)为0.75,这是一个折中的取值,也就是说,默认情况下,数组大小(DEFAULT_INITIAL_CAPACITY)为16 ,那么当 HashMap 中元素个数超过 16 * 0.75 = 12 (这个值就是代码中的 threshold值,也叫临界值)的时候,就把数组的大小扩展为 2 * 16 = 32 ,即扩大 1倍 ,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作。所以在开发中如果我们可以预估计其存储的数据量,也就是 HashMap中存储元素的个数,那么就调用其HashMap(int num) 设定存储容量的大小,减少扩容次数,提高 HashMap的性能 。

  树形化

  当HashMap中的其中一个链的对象个数如果达到了8个,此时如果capacity没有达到64,那么HashMap会先扩容解决,如果已经达到了64,那么这个链会变成树,结点类型由Node变成TreeNode类型。当然,如果当映射关系被移除后,下次resize方法时判断树的结点个数低于6个,也会把树再转为链表。

  补充:

  关于映射关系的key是否可以修改 ???

  answer:不要修改,映射关系存储到 HashMap 中会存储 key 的 哈希值 ,这样就不用每次查找时,重新计算每一个 Entry 或 Node (TreeNode)的 哈希值了,因此如果已经 put 到 Map 中的映射关系,再修改 key 的属性,而这个属性有参与 hashCode值的计算,那么会导致匹配不上。

  为什么HashMap扩容时,不是数组满了的时候扩容而是达到一个的 0.75 的额度才扩容 ???

  因为HashMap 集合的底层时由 链表 + 数组 + 树 构成的。由于链表的存在,HashMap 当中的数组不一定会存储满了。

  以及涉及到 HashMap 集合性能最优的效果,散列均匀分布,所以是到达一定额度 0.75 是最好的情况了.

  负载因子值的大小,对HashMap有什么影响 ???

  

/**

 

  * The load factor used when none specified in constructor.

  static final float DEFAULT_LOAD_FACTOR = 0.75f; // HashMap的默认加载因子

  

 

  负载因子的大小决定了HashMap的数据密度。

  负载因子越大密度越大,发生碰撞的几率越高,数组中的链表越容易长,

  造成查询或插入时的比较次数增多,性能会下降。

  负载因子越小,就越容易触发扩容,数据密度也越小,意味着发生碰撞的

  几率越小,数组中的链表也就越短,查询和插入时比较的次数也越小,性能会更高。但是会浪费一定的内容空间。而且经常扩容也会影响性能,建议初始化预设大一点的空间。

  按照其他语言的参考及研究经验,会考虑将负载因子设置为0.7~0.75,此时平均检索长度接近于常数。

  3.3.5 总结:JDK1.8 相较于之前的变化:

  
JDK8 :HashMap map = new HashMap() ,默认情况下,是不会先创建长度为 16 的 数组的,而是首先调用 map.put() 添加键值对的时候创建 长度为 16的数组(类比:单例模式中的饿汉式)。 JDK7 则是默认先创建了一个长度为 16的数组(类比:单例模式中的懒汉式)。

  
put(Key1,Value1) 添加键值对时,JDK7 是添加到链表上的头部(数组上),JDK8 是添加到链表的尾部(不在数组上),简称:七上八下。

  
jdk7 底层结构只有:数组 + 链表,jdk8 中底层结构: 数组 + 链表 + 红黑树

  当数组的某一个索引位置上的元素以链表形式存在的数据个数 8 且当前数组的长度 64时,此时此索引位置上的所有数据改为使用“红黑树”存储。当小于 8 时,有会变成链表的形式存储。

  
4. Map实现类之二:LinkedHashMap

  LinkedHashMap 是 HashMap 的子类,所以 LinkedHashMap 继承了 HashMap 的特点:其中的 key 是无序,不可重复的,其 Key 存储的元素类必须重写 eqauls() 和 hashCode() 方法。同样的 Key 值是存储在 Set 集合当中的,而Value 则是存储在 Collection 集合当中的。

  LinkedHashMap 是在 HashMap 存储结构的基础上,使用了一对双向链表来记录添加元素的顺序的,所以你添加元素数据的顺序是怎样的,取元素数据的顺序就是怎样的。

  与 LinkedHashSet 类似, LinkedHashMap 可以维护 Map 的迭代顺序:迭代顺序与 Key-Value 键值对的插入顺序一致,简单的说就是:存取顺序一样。

  LinkedHashMap 是继承了 HashMap 其常用的方法是一样的,这里就不多说明了。不同的是HashMap 底层的内部类是 Node ,而LinkedHashMap 底层的内部类是Entry ,该内部类继承了 HashMap.Node K,V

  HashMap中的内部类:Node

  LinkedHashMap中的内部类:Entry

  举例:

  

import java.util.Iterator;

 

  import java.util.LinkedHashMap;

  import java.util.Map;

  import java.util.Set;

  public class LinkedHashMapTest {

   public static void main(String[] args) {

   // 创建 LinkedHashMap 集合对象

   LinkedHashMap String,Integer linkedHashMap = new LinkedHashMap String, Integer

   // 添加元素数据(键值对)

   linkedHashMap.put("lihua",99);

   linkedHashMap.put("zhangsan",89);

   linkedHashMap.put("lisi",79);

   linkedHashMap.put("wangwu",69);

   // 遍历 LinkedHashMap 集合

   // 获取到key-value 存储的 Set Entry 内部类集合对象

   Set Map.Entry String, Integer entries = linkedHashMap.entrySet();

   // 获取到该 Set Entry 内部类集合的迭代器

   Iterator Map.Entry String, Integer iterator = entries.iterator();

   while(iterator.hasNext()) {

   Map.Entry String, Integer entry = iterator.next();

   System.out.println(entry.getKey() + "--- " + entry.getValue());

  

 

  5. Map实现类之三:TreeMap

  
TreeMap 存储 Key-Value 键值对时,需要根据 key-value 键值对进行排序,TreeMap 可以保证所有的 key-value 键值对处于有序状态。

  
TreeSet 底层 就是由 TreeMap 构成的,new TreeSet 底层实际上说就是 new TreeMap 集合存储数据,向 TreeSet 中添加数据就是向 TreeMap 集合中添加数据。

  
TreeMap 的 排序是对 Key 的内容进行排序的,其中的 Key 值内部是由 Set 集合存储的,无序,不可重复性,所存储类必须重写 equals() 和 hashCode() 方法。因为会自动排序,所以还需要实现排序:两种方式一种是:

  自然排序: TreeMap 的所有的 Key 必须实现(实现 java.lang.Comparable的接口,而且所有 的 Key 应该是同一个类的对象(因为不是同一类型无法比较判断),否则将会抛出 ClasssCastException自然排序,重写其中的 compareTo()抽象方法) 。在Java当中所有的包装类和String都实现了该 java.lang.Comparable接口。所以一般要实现该接口的都是自定的类。

  定制排序: 创建 TreeMap 时,传入一个 Comparator 对象,该对象负责对 TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现 Comparable 接口

  

public TreeMap(Comparator ? super K comparator)v // 构造一个新的,空的树图,按照给定的比较器排序。

 

  

 

  关于自然排序与 定制排序 的详解内容大家可以移步至:

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: