Map

Map 是一种键-值对(key-value)集合,Map 集合中的每一个元素都包含一个键对象(key)和一个值(value)对象。其中,键对象不允许重复,而值对象可以重复,并且值对象还可以是 Map 类型的,就像数组中的元素还可以是数组一样。

方法:



方法解释:

        //将集合元素清空处理hashMap.clear();//boolean containsKey(Object key)  判断集合中是否存在指定的键 key  存在返回true  不存在返回falseboolean key = hashMap.containsKey("战三");//boolean containsValue(Object value) 判断集合中是否存在指定的值value  //存在返回true  不存在返回falsehashMap.containsValue("zhangsan");//V get(Object key) 通过键key获取valueString s = hashMap.get("张杰");
   HashMap<String,String> hashMap=new HashMap<>();hashMap.put("孙悟空","孙行者");hashMap.put("露娜","紫霞仙子");hashMap.put("甄姬","游园惊梦");//put操作时,如果插入的key已将存在,新插入的value会覆盖掉新的valuehashMap.put("甄姬","花好月圆");//迭代键值对Iterator<Map.Entry<String, String>> iterator1 = hashMap.entrySet( ).iterator( );while (iterator1.hasNext()){System.out.println(iterator1.next());}

运行结果:

遍历方法:

  //迭代键值对Iterator<Map.Entry<String, String>> iterator1 = hashMap.entrySet( ).iterator( );while (iterator1.hasNext()){System.out.println(iterator1.next());}//迭代keyIterator<String> iterator2 = hashMap.keySet( ).iterator( );while (iterator2.hasNext()){System.out.println(iterator2.next());}//迭代valueIterator<String> iterator3 = hashMap.values( ).iterator( );while (iterator3.hasNext()){System.out.println(iterator3.next());}rum();

hashmap

特点:

1、存储的数据是键值对形式,key不能重复,value值可以重复
2、key和value都可以为null
3、不能保证内部元素的顺序
4、底层数据结构是哈希表

哈希表:key通过哈希函数映射到特定值的数据结构
哈希冲突:哈希函数f(x) ,f(m) =f(n) ,m不等于n
哈希函数:直接哈希,取模。。。
哈希冲突:链地址法,探测法(线性探测、随机探测)。。。

链地址法:如图所示:

源码研究:

继承关系:

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

HashMap继承自AbstractMap,该抽象类对Map接口的常用方法做了实现,方便子类复用
实现类Map接口,具有Map中提供的所有方法
实现Cloneable和Serializable接口

构造函数:

//通过初始容量和加载因子来实例HashMap对象
public HashMap(int initialCapacity, float loadFactor) {//对参数合法性校验if (initialCapacity < 0)throw new IllegalArgumentException("Illegal initial capacity: " +initialCapacity);if (initialCapacity > MAXIMUM_CAPACITY)initialCapacity = MAXIMUM_CAPACITY;if (loadFactor <= 0 || Float.isNaN(loadFactor))throw new IllegalArgumentException("Illegal load factor: " +loadFactor);this.loadFactor = loadFactor;threshold = initialCapacity;init();}//通过初始容量来实例HashMap,加载因子是默认值0.75 public HashMap(int initialCapacity) {this(initialCapacity, DEFAULT_LOAD_FACTOR);}//通过无参构造实例化  默认初始容量16 默认加载因子0.75public HashMap() {this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR);}//通过map集合实例构造HashMap实例public HashMap(Map<? extends K, ? extends V> m) {this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,DEFAULT_INITIAL_CAPACITY),DEFAULT_LOAD_FACTOR);//初始化table、threshold参数,实例化哈希表inflateTable(threshold);//将map结合中的键值对存放到HashMap中putAllForCreate(m);}

属性及默认值:

//默认初始容量 :值为16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
//最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;//默认的加载因子:0.75
static final float DEFAULT_LOAD_FACTOR = 0.75f;//默认空表
static final Entry<?,?>[] EMPTY_TABLE = {};//存放数据 table属性,是entry类型数组
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;//存放的键值对个数
transient int size;//扩容阈值   ,在扩容时判断  threshold =table.length()*loadFactor
int threshold;//加载因子
final float loadFactor;//版本控制
transient int modCount;//key哈希相关
static final int ALTERNATIVE_HASHING_THRESHOLD_DEFAULT = Integer.MAX_VALUE;
//key哈希相关
transient int hashSeed = 0;

底层数据结构:

//存放数据位置
transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;static class Entry<K,V> implements Map.Entry<K,V> {final K key;V value;Entry<K,V> next;int hash;}

扩容机制
扩容时机:在size>threshold时,会进行扩容

常见方法:

插入操作:
    public V put(K key, V value) {if (table == EMPTY_TABLE) {//当table数组为空时,进入初始化table数据,当第一次调用put操作会进入inflateTable(threshold);}//key为null,将数据存放在0号卡槽位置if (key == null)return putForNullKey(value);//key不为null的处理//通过key找到对应的存放卡槽位置int hash = hash(key);int i = indexFor(hash, table.length);for (Entry<K,V> e = table[i]; e != null; e = e.next) {//通过位置i找到卡槽i位置的数据链表,从头遍历,找key是否存在//判断条件是hash和key,相等值更新Object k;if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {V oldValue = e.value;e.value = value;e.recordAccess(this);return oldValue;}}//key在i位置不存在,需要新增entry节点存放数据modCount++;addEntry(hash, key, value, i);return null;}private V putForNullKey(V value) {//key为null将哈希位置为0号位置,需要遍历0号卡槽链表、判断key为null是否存在//存在将entry中value进行跟新,返回旧的value中//不存在则新建entry实体,存放put的数据for (Entry<K,V> e = table[0]; e != null; e = e.next) {if (e.key == null) {V oldValue = e.value;e.value = value;e.recordAccess(this);return oldValue;}}modCount++;addEntry(0, null, value, 0);return null;}//通过key哈希找到对应卡槽
final int hash(Object k) {int h = hashSeed;if (0 != h && k instanceof String) {return sun.misc.Hashing.stringHash32((String) k);}h ^= k.hashCode();// This function ensures that hashCodes that differ only by// constant multiples at each bit position have a bounded// number of collisions (approximately 8 at default load factor).h ^= (h >>> 20) ^ (h >>> 12);return h ^ (h >>> 7) ^ (h >>> 4);}//通过key的哈希值找到在哈希表中的位置
static int indexFor(int h, int length) {// assert Integer.bitCount(length) == 1 : "length must be a non-zero power of 2";/* 容量是160001 0000     160000 1111     151010 1001-------------0000 1001    9 */return h & (length-1);}void addEntry(int hash, K key, V value, int bucketIndex) {//当存放数据size大于扩容阈值threshold,进行扩容if ((size >= threshold) && (null != table[bucketIndex])) {//对HashMap进行2倍的扩容resize(2 * table.length);hash = (null != key) ? hash(key) : 0;//获取当前key应该插入的新卡槽位置bucketIndex = indexFor(hash, table.length);}createEntry(hash, key, value, bucketIndex);}//扩容
void resize(int newCapacity) {Entry[] oldTable = table;int oldCapacity = oldTable.length;if (oldCapacity == MAXIMUM_CAPACITY) {threshold = Integer.MAX_VALUE;return;}//新创建数组为原来的2倍Entry[] newTable = new Entry[newCapacity];//将原map中的每一个entry实体进行重新哈希到新的map中transfer(newTable, initHashSeedAsNeeded(newCapacity));table = newTable;//threshold = table.length*loadFactorthreshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);}//采用头插法将数据插入
void createEntry(int hash, K key, V value, int bucketIndex) {Entry<K,V> e = table[bucketIndex];table[bucketIndex] = new Entry<>(hash, key, value, e);size++;}

put操作步骤
1、先判断key是否为null,特殊处理,存放在哈希表的0号卡槽位置
2、key为null的数据是否存在,遍历0号卡槽位置的链表,如果存在(==)则更新value,返回
3、如果不存在,新建节点(addentry)
4、key不为null,通过key来计算哈希值,找到在哈希表的卡槽位置(hash,indexFor)
5、在对应卡槽获取链表,遍历找key是否存在,如果存在(hash&&equals)则更新value,返回
6、key在链表不存在,新建节点(addEntry)
7、考虑是否扩容(size>threshold),需要扩容,将新的大小为原来的2倍,然后将原哈希表中的数据
都重新哈希到新的哈希表中, 并更新当前插入节点的卡槽位置
8、 采用头插入将新entry节点插入到给定的卡槽位置

获取元素:
    public V get(Object key) {//key为null,直接到0号卡槽位置获取结果if (key == null)return getForNullKey();//key不为nullEntry<K,V> entry = getEntry(key);return null == entry ? null : entry.getValue();}private V getForNullKey() {//如果map为空,返回nullif (size == 0) {return null;}//在0号卡槽位置,对链表遍历,查找key为null是否存在,存在则找entry中value返回,否则返回nullfor (Entry<K,V> e = table[0]; e != null; e = e.next) {if (e.key == null)return e.value;}return null;}//通过key找到对应entry实例
final Entry<K,V> getEntry(Object key) {if (size == 0) {return null;}//通过key的哈希找到key对应卡槽位置 int hash = (key == null) ? 0 : hash(key);for (Entry<K,V> e = table[indexFor(hash, table.length)];e != null;e = e.next) {Object k;if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))return e;}return null;}

查询过程:
1、如果key为null,在0号卡槽位置遍历链表查询,key存在则返回entry的value,否则返回null
2、如果key不为null,对key进行哈希,找到对应的卡槽,遍历链表,判断key是否存在(hash,key.equals),返回entry的value否则返回null

删除元素:
    public V remove(Object key) {Entry<K,V> e = removeEntryForKey(key);return (e == null ? null : e.value);}final Entry<K,V> removeEntryForKey(Object key) {//如果集合为空,直接返回nullif (size == 0) {return null;}//通过key哈希找到对应卡槽(key为null卡槽为0)int hash = (key == null) ? 0 : hash(key);int i = indexFor(hash, table.length);Entry<K,V> prev = table[i];Entry<K,V> e = prev;//删除节点即解决单向链表的删除问题:解决思路:给定两个指针,两指针一前一后,前指针表示要删除的节点,、  //通过后一个指针来将节点和前节点指针的next建立联系while (e != null) {Entry<K,V> next = e.next;Object k;if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k)))) {modCount++;size--;//如果删除的是头结点,将后一个节点作为当前卡槽的头结点if (prev == e)table[i] = next;else//后一个指针prex来将节点和前节点指针的next建立联系prev.next = next;e.recordRemoval(this);return e;}prev = e;e = next;}return e;}

删除过程:
1、通过key哈希来找到卡槽位置(key为null在0号卡槽)
2、对应卡槽的链表进行遍历查找,给定两个指针一前一后,前指针找到要删除节点,后指针建立和下一个节点关系

特点:

1、数据结构:哈希表(数组+链表)
2、默认数组大小:16
3、扩容机制:大小为原来数组长度的2倍
4、扩容因子:0.75f
5、扩容条件:0.75*数组的长度
6、线程安全:该集合是线程不安全的
7、储存数据的类型为键值对(key和value)
8、key和value都可以为null
9、储存的数据是无序的
10、key不能重复,value可以重复
11、key相同则会进行值覆盖

应用场景:数据统计

LinkedHashMap:

通过集合框架图可知:LinkedHashMap属于HashMap的子实现类

public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>

基本特点:

1、key不能重复、value是能重复
2、LinkedHashMap是插入有序的/访问有序 accessOrder:true 访问有序 false:插入有序
3、底层的数据结构是哈希表
4、key和value都能为null

有序的实现:

数据结构:

通过LinkedHashMap的继承关系是继承自HashMap,HashMap的属性及方法被继承
那table属性及entry类型都会被继承,其内部也是一个哈希表结构

其他属性:
//头结点
private transient Entry<K,V> header;//accessOrder  控制是插入还是访问有序    默认是false即插入有序  ture:访问有序private final boolean accessOrder;class Entry<K,V> extends HashMap.Entry<K,V> {Entry<K,V> before, after;
}

LinkedHashMap中entry类继承自HashMap.Entry,即具有key\value\hash\next等属性
另还新增了两属性是entry类型的 before和after

LInkedHashMap的实现是哈希表+双向链表

通过哈希表结构存储数据
双向链表在哈希表的基础上来讲节点按照插入或者访问的顺序进行连接

常见方法:
插入:
//父类HashMap实现
public V put(K key, V value) {if (table == EMPTY_TABLE) {//如果table为空,按照默认16大小初始化哈希表inflateTable(threshold);}if (key == null)return putForNullKey(value);int hash = hash(key);int i = indexFor(hash, table.length);for (Entry<K,V> e = table[i]; e != null; e = e.next) {Object k;if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {V oldValue = e.value;e.value = value;e.recordAccess(this);return oldValue;}}modCount++;addEntry(hash, key, value, i);return null;}//linkedhashmap中entry中实现
void recordAccess(HashMap<K,V> m) {LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;if (lm.accessOrder) {lm.modCount++;remove();addBefore(lm.header);}}//如果当前是访问有序,将当前节点从双向链表中删除,然后将其插入head的before处理//linkedHashmap中实现void addEntry(int hash, K key, V value, int bucketIndex) {super.addEntry(hash, key, value, bucketIndex);// Remove eldest entry if instructedEntry<K,V> eldest = header.after;if (removeEldestEntry(eldest)) {removeEntryForKey(eldest.key);}}
HashMap中addentry
void addEntry(int hash, K key, V value, int bucketIndex) {if ((size >= threshold) && (null != table[bucketIndex])) {resize(2 * table.length);hash = (null != key) ? hash(key) : 0;bucketIndex = indexFor(hash, table.length);}createEntry(hash, key, value, bucketIndex);}//linkedHashMap中createEntryvoid createEntry(int hash, K key, V value, int bucketIndex) {HashMap.Entry<K,V> old = table[bucketIndex];Entry<K,V> e = new Entry<>(hash, key, value, old);table[bucketIndex] = e;//处理双向链表,插入到header的before位置e.addBefore(header);size++;}

LinkedHashMap的插入中实现和HashMap的实现的区别点
1、在插入时且key存在时,考虑如果是访问有序,将该节点从双向链表插入放入最后位置
2、在key不存在考虑新增entry节点时,在插入插入哈希表后,还需要维护双向链表,即插入双向链表的尾部

应用场景:

统计数据,按照访问顺序统计

HashTable:

继承关系:

    public class Hashtable<K,V>extends Dictionary<K,V>implements Map<K,V>, Cloneable, java.io.Serializable

HashMap的继承关系:

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

Dictionary实现:

    public abstract class Dictionary<K,V> {public Dictionary() {}abstract public int size();abstract public boolean isEmpty();abstract public Enumeration<K> keys();  //获取键的集合abstract public Enumeration<V> elements(); //获取值的集合abstract public V put(K key, V value);abstract public V remove(Object key);
}

HashTable和HashMap的异同点:

相同点:
1、底层数据机构是哈希表(JDK 1.7)
2、key-value键值对中,key是不能重复的,value是可以重复的
3、HashMap和Hashtable都是插入无序的

不同点:
1、HashTable继承自Dictionary类,该类是比较早期提供的map父类,现推荐使用AbstractMap类
2、HashTable的默认初始值是11
3、HashTable是线程安全的(通过在方法上添加synchronized关键)
4、HashTable中key和value都不能为null
5、HashTable中对key的哈希过程和HashMap是不一样的
6、HashTable的扩容按照2倍加1大小扩容((oldCapacity << 1) + 1

TreeMap集合:

基本特点:

treeMap特点
1、key按照大小顺序排序,默认的是从小到大
2、key不能为null,value是可以为null
3、key不能重复、value可以重复

TreeMap是如何做到key有序的?

TreeMap底层数据结构是红黑树
时间复杂度O(log n) O(n) o(1)

TreeMap继承关系:

    public class TreeMap<K,V>extends AbstractMap<K,V>implements NavigableMap<K,V>, Cloneable, java.io.Serializable

treemap 实现NavigableMap接口,支持一系列的导航方法,返回具有顺序的key集合等

而NavigableMap接口声明如下:

public interface NavigableMap<K,V> extends SortedMap<K,V>

NavigableMap接口继承自SortedMap接口,SortedMap接口具有排序功能,具有比较器类Comparator属性

比较器类说明:

Comparable和Comparator比较
两个比较器都是接口:
Comparable接口中提供方法:

    public interface Comparable<T> {public int compareTo(T o);
}

接口中提供了compare方法,
该方法返回结果为大于0,等于0,小于0
类外部实现,自定义实现比较过程使用该接口

使用场景:

对数据进行排序选择TreeMap

weakHashMap:

weakHashMap中没有特殊的数据结构,主要是为了优化JVM,是JVM在垃圾回收时能更智能的回收无用的对象

weakHashMap主要适用于缓存的场景,当一个键对象被垃圾回收器回收时,响应的值对象会从map中删除,weakhashmap能够节约内存空间,缓存一些非必要的数据

weakhashmap与其他的map的区别在于key是一个弱引用类型,其他的map中的key是一个强引用类型,
在Java中,引用有4中类型:强(Strong)引用、软引用(soft)、弱引用(weak)、虚引用(phantom)
引用类型:强引用
强引用是常见的引用类型,通常通过new形式创建的对象都是强引用对象
强引用作用的对象无论何时都不会被GC清理掉
只要强引用存在,GC永不会回收掉被引用的对象,通过关键字new创建的对象所关联的引用就是强引用,只要这个强引用还指向某一个对象,那就说明对象还一直存活这,垃圾回收器就不会碰,即使当内存不足是,JVM抛出OOM(OutOfMemoryError)异常也不会也不会回收强引用作用对象

软引用、若引用、虚引用这些都存在java.lang.ref包中,父类为Reference类
Reference抽象父类的构造函数

      //referent为引用指向的对象Reference(T referent) {this(referent, null);}//ReferenceQueue 可以理解为队列,在GC之前,将引用的对象放入Queue中,方便清理引用对象本身Reference(T referent, ReferenceQueue<? super T> queue) {this.referent = referent;this.queue = (queue == null) ? ReferenceQueue.NULL : queue;}

Map接口及其实现类相关推荐

  1. Collection 和 Map接口及其实现类总结

    Collection 和 Map接口及其实现类总结 Collection接口 Collection是最基本的集合接口,一个Collection代表一组Object,即Collection的元素(Ele ...

  2. Java讲课笔记23:Map接口及其实现类

    文章目录 零.本讲学习目标 1.了解Map集合的常用方法 2.掌握HashMap和TreeMap的使用 3.掌握Properties集合的使用 4.掌握Map集合遍历 一.Map接口 1.Map接口概 ...

  3. Java集合篇:Map接口、Map接口的实现类、Collections集合工具类

    目录 一.Map接口 1.1 Map接口概述 1.2 Map接口常用功能 二.Map接口的实现类 2.1 Map实现类之一:HashMap 2.1.1 HashMap概述 2.1.2 HashMap的 ...

  4. java hashedmap_Java基础 - Map接口的实现类 : HashedMap / LinkedHashMap /TreeMap 的构造/修改/遍历/ 集合视图方法/双向迭代输出...

    import java.util.*; /**一:Collection接口的 * Map接口: HashMap(主要实现类) : HashedMap / LinkedHashMap /TreeMap ...

  5. set删除一个元素时间复杂度_set/map接口及其实现类

    ---------------[ 感谢小郡提供的图片] 1.Map接口/Hashtable.HashMap.TreeMap实现类 Hashtable.HashMap.TreeMap 都是最常见的一些 ...

  6. Java集合(7)--Map接口的实现类HashMap、LinkHashMap、TreeMap和Properties

    文章目录 HashMap类 LinkedHashMap类 TreeMap类 Hashtable类 Properties类 HashMap类 1.HashMap类概述 HashMap是 Map 接口使用 ...

  7. Collection集合类和Map接口各实现类详解

    Java的集合类(collection接口和Map) 一.集合概述 集合:集合是java中提供的一种容器,可以用来存储多个数据. 集合和数组既然都是容器,它们有啥区别呢? 数组的长度是固定的.集合的长 ...

  8. 集合框架详解之Set、Map接口与实现类

    集合框架 1.Set集合 1.1Set集合概述 Set实现类:HashSet类.TreeSet类 特点:无序.无下标.元素不可重复 方法:全部继承自Collection中的方法 1.11 Set接口的 ...

  9. JAVA集合Connection接口和Map接口常用的类及区别

    JAVA集合详解 文章目录 JAVA集合详解 前言 一.集合是什么? 1. 集合类 2.集合和数组的区别: 二.使用步骤 一.Connection接口(单列集合) 1.List和Set的优缺点 2.总 ...

  10. Map接口的实现类HashMap的操作

    3中遍历HashMap方式 package C12_21;import java.util.Collection; import java.util.HashMap; import java.util ...

最新文章

  1. python数据库查询不出结果_记一次pymysql查询不到表中最新插入的数据的问题
  2. vmware克隆server2008R2造成SID冲突
  3. SAP登陆界面TITLE修改方法(Method of SAP Logon GUI Title Modification)
  4. Delete Edges
  5. int转为string类型方法
  6. 随想录(c语言的优缺点)
  7. kotlin半生对象_Kotlin单一对象,Kotlin伴侣对象
  8. 「leetcode」203.移除链表元素:听说用虚拟头节点会方便很多?
  9. bzoj4695 最假女选手(势能线段树/吉司机线段树)题解
  10. 修改计算机系统参数软件,机器码修改专家(修改电脑机器码工具) v2.0官方版
  11. double IE之H3CIE之路--我的H3CIE考试经历
  12. 我“药水哥”硬气了 改行‘程序员’了
  13. 谷歌又放大招 Disco Diffusion!AI生成超高质量绘画!
  14. 重装系统后无法连接到网络?
  15. 程序员除了写代码,还能做哪些副业?
  16. 现在Web前端工程师年薪区间是多少?
  17. 自动化设备远程监控系统软件
  18. spfa判断负环( 负环判定 + spfa )
  19. 看看咱是如何用MATLAB白嫖遥遥领先于同行的神仙级翻译工具 — DeepL
  20. 如何实现分库分表?怎么配置?

热门文章

  1. matlab仿真integrator,弹球的仿真 - MATLAB Simulink - MathWorks 中国
  2. 十一条Python学习路线,推荐收藏
  3. spring事务传播机制之《REQUIRED》
  4. 【分子动力学模拟】centos7使用gmx_MMPBSA时使用gmx_MMPBSA_ana时无法调用pyqt5
  5. 2019烟台计算机副高职称报名时间,烟台市公共企事业单位信息公开 政策文件 2019年度职称评审工作日程安排...
  6. 信息提取(Information Extraction)
  7. Vmware私有云平台搭建(1)
  8. 中兴光纤猫 F460 V3.0破解
  9. SCORM的对手——LOM
  10. An invalid domain [.test.com] was specified for this cookie 原因分析