Map接口实现类HashMap源码及详解

一.简介

 

HashMap实际上是一个“链表散列”的数据结构。JDK1.8以前:HashMap底层是用数组+双向链表实现的。JDK1.8及以后:HashMap底层是用数组+双向链表+红黑树实现的。

 

数组:存储区间连续,占用内存严重,寻址容易,插入删除困难; 


链表:存储区间离散,占用内存比较宽松,寻址困难,插入删除容易;

 
HashMap综合应用了这两种数据结构,实现了寻址容易,插入删除也容易。 

 

 

 

 

 

 

二.HashMap常用方法

 

1.put(K key, V value):以键值对的形式向HashMap中添加元素

HashMap<String, String> hashMap = new HashMap<String, String>();
hashMap.put("1", "张三");

 

 

2.putAll(Map<? extends K, ? extends V> m):将一个Map全部添加到另一个Map

HashMap<String, String> hashMap = new HashMap<String, String>();
hashMap.put("1", "张三");

HashMap<String, String> allHashMap = new HashMap<String, String>();
allHashMap.putAll(hashMap);

 

 

3.size():获取集合长度

HashMap<String, String> hashMap = new HashMap<String, String>();
hashMap.put("1", "张三");
int num = hashMap.size();

 

 

4.isEmpty():集合是否为空

HashMap<String, String> hashMap = new HashMap<String, String>();
boolean b = hashMap.isEmpty();

 

 

5.get(Object key):根据键获取对应的值

HashMap<String, String> hashMap = new HashMap<String, String>();
String value = hashMap.get("1");

 

 

6.containsKey(Object key):集合中是否包含key

HashMap<String, String> hashMap = new HashMap<String, String>();
hashMap.put("1", "张三");
hashMap.put("2", "李四");
hashMap.put("3", "王五");
hashMap.put("4", "赵六");

boolean b = hashMap.containsKey("3");
boolean c = hashMap.containsKey("6");
Log.d("TAG", "b----:" + b);
Log.d("TAG", "c----:" + c);
b----:true
c----:false

 

 

7.containsValue(Object value):集合中是否包含value

HashMap<String, String> hashMap = new HashMap<String, String>();
hashMap.put("1", "张三");
hashMap.put("2", "李四");
hashMap.put("3", "王五");
hashMap.put("4", "赵六");

boolean b = hashMap.containsValue("张三");
boolean c = hashMap.containsValue("6");
Log.d("TAG", "b----:" + b);
Log.d("TAG", "c----:" + c);
b----:true
c----:false

 

 

8.remove(Object key):删除集合中key对应的value

HashMap<String, String> hashMap = new HashMap<String, String>();
hashMap.put("1", "张三");
hashMap.put("2", "李四");
hashMap.put("3", "王五");
hashMap.put("4", "赵六");

int num1 = hashMap.size();
Log.d("TAG", "num1----:" + num1);

hashMap.remove("2");
int num2 = hashMap.size();
Log.d("TAG", "num2----:" + num2);
num1----:4
num2----:3

 

 

9.clear():清空集合元素

HashMap<String, String> hashMap = new HashMap<String, String>();
hashMap.put("1", "张三");
hashMap.put("2", "李四");
hashMap.put("3", "王五");
hashMap.put("4", "赵六");

int num1 = hashMap.size();
Log.d("TAG", "num1----:" + num1);

hashMap.clear();
int num2 = hashMap.size();
Log.d("TAG", "num2----:" + num2);
num1----:4
num2----:0

 

 

10.keySet():遍历集合 获取key 然后通过获取的key获得相应的value

public class CollectionActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_collection);

        initArrayList();
    }

    public void initArrayList() {

        HashMap<String, String> hashMap = new HashMap<String, String>();
        hashMap.put("1", "张三");
        hashMap.put("2", "李四");
        hashMap.put("3", "王五");
        hashMap.put("4", "赵六");

        Set<String> set = hashMap.keySet();
        Iterator<String> iterator = set.iterator();
        while (iterator.hasNext()) {
            String key = iterator.next();
            String value = hashMap.get(key);
            Log.i("TAG", "key----:" + key + "----value----:" + value);
        }

    }

}
I/TAG: key----:1----value----:张三

I/TAG: key----:2----value----:李四

I/TAG: key----:3----value----:王五

I/TAG: key----:4----value----:赵六

 

 

11.values():遍历集合 获取value

public class CollectionActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_collection);

        initArrayList();
    }

    public void initArrayList() {

        HashMap<String, String> hashMap = new HashMap<String, String>();
        hashMap.put("1", "张三");
        hashMap.put("2", "李四");
        hashMap.put("3", "王五");
        hashMap.put("4", "赵六");
        hashMap.put("5", "980");

        Collection<String> collection = hashMap.values();
        Iterator<String> iterator = collection.iterator();
        while (iterator.hasNext()) {
            String value = iterator.next();
            Log.i("TAG", "value----:" + value);
        }

    }

}
I/TAG: value----:张三

I/TAG: value----:李四

I/TAG: value----:王五

I/TAG: value----:赵六

I/TAG: value----:980

 

 

12.entrySet():遍历集合 直接获取key value

public class CollectionActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_collection);

        initArrayList();
    }

    public void initArrayList() {

        HashMap<String, String> hashMap = new HashMap<String, String>();
        hashMap.put("1", "张三");
        hashMap.put("2", "李四");
        hashMap.put("3", "王五");
        hashMap.put("4", "赵六");
        hashMap.put("5", "980");

        Set<Map.Entry<String, String>> entrySet = hashMap.entrySet();
        Iterator<Map.Entry<String, String>> iterator = entrySet.iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, String> map = iterator.next();
            String key = map.getKey();
            String value = map.getValue();
            Log.i("TAG", "key----" + key + "----value----:" + value);
        }

    }

}
I/TAG: key----1----value----:张三

I/TAG: key----2----value----:李四

I/TAG: key----3----value----:王五

I/TAG: key----4----value----:赵六

I/TAG: key----5----value----:980

 

 

 

 

 

 

三.源码分析

 

我们从HashMap的构造方法开始我们的源码之旅。

 

源码分析之构造方法

 

讲解前,先列举一下HashMap类常用的变量和类,因为下面的源码分析都会用到。

【1】int threshold;
 
 
含义:The next size value at which to resize 。
 
 
含义:容器所能容纳的Key-Value对。也称为阈值。
 
 
计算:capacity * load factor


************************************************
 
【2】transient int modCount;
 
含义:The number of times this HashMap has been structurally modified。
 
 
含义:HashMap结构修改的次数。主要用于在迭代器遍历集合时 判断fast-fail 来确保多线程同步问题。具体下面会讲解
 

************************************************
 
【3】final float loadFactor;
 
 
含义:The load factor for the hash table。
 
含义:负载因子。表示可最大容纳数据数量的比例。


*************************************************
 
【4】transient int size;
 
含义:The number of key-value mappings contained in this map。
 
 
含义:HashMap中实际存入的键值对数量。


*************************************************

【5】transient Node<K,V>[] table;

含义:存放数据的数组

 

1.无参构造方法

使用

HashMap<String, String> map = new HashMap<>();
map.put("2", "张三");

 

源码

/**
  * Constructs an empty <tt>HashMap</tt> with the default initial capacity
  * (16) and the default load factor (0.75).
*/

public HashMap() {
     this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
//默认加载因子0.75
static final float DEFAULT_LOAD_FACTOR = 0.75f;

也就是说,使用默认无参构造方法时只是指定默认的加载因子为0.75f

 

 

2.指定大小的构造方法

使用

HashMap<String, String> map = new HashMap<>(5);
map.put("2", "张三");

 

源码

/**
 * Constructs an empty <tt>HashMap</tt> with the specified initial
 * capacity and the default load factor (0.75).
 *
 * @param  initialCapacity the initial capacity.
 * @throws IllegalArgumentException if the initial capacity is negative.
 */
public HashMap(int initialCapacity) {
    this(initialCapacity, DEFAULT_LOAD_FACTOR);
}

 

内部调用两个参数的构造方法

/**
 * Constructs an empty <tt>HashMap</tt> with the specified initial
 * capacity and load factor.
 *
 * @param  initialCapacity the initial capacity
 * @param  loadFactor      the load factor
 * @throws IllegalArgumentException if the initial capacity is negative
 *         or the load factor is nonpositive
 */
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;
    this.threshold = tableSizeFor(initialCapacity);
}
static final int MAXIMUM_CAPACITY = 1 << 30;

也就是说,使用指定大小的构造方法时,内部调用两个参数的构造方法。

构造方法中 需要校验传参的大小。

<1> 传参的大小 小于0 抛出异常

<2> 传参的大小 大于MAXIMUM_CAPACITY即(1 << 30==1073741824) 则设置传参大小为1073741824 也就是说HashMap的默认可以容纳的Key-Value对是1073741824

<3> 其他值,就使用传参的大小。

 

然后,负载因子是默认的0.75f。

 

然后使用tableSizeFor()方法计算threshold的值

tableSizeFor()方法源码

/**
 * Returns a power of two size for the given target capacity.
 */
static final int tableSizeFor(int cap) {
    int n = cap - 1;
    n |= n >>> 1;
    n |= n >>> 2;
    n |= n >>> 4;
    n |= n >>> 8;
    n |= n >>> 16;
    return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}

此方法的含义就是,将传参的大小变成最接近2的n次幂的数。比如传入7计算后为8 传入15计算后16  传入16计算后16。也就是说threshold(容器中所能容纳的Key-Value对)的值一定是2的n次幂

 

 

 

源码分析之put()方法

 

使用

HashMap<String, String> map = new HashMap<>(5);
map.put("2", "张三");

 

源码

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

内部调用putVal方法。传五个参数。

第一个参数需要根据Key计算哈希值。调用hash()方法。这个hash()方法就是常常说到的哈希函数

 

 

源码分析之哈希函数

 

hash()方法源码

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

说明:key.hashCode():就是拿到Key对应的hashCode。

 

如果Key为空 则 返回0。

如果Key不为空 则返回Key的hashCode和hashCode>>>16异或值。

 

大概的原理:

就是低16位是和高16位进行异或,高16位保持不变。一般的数组长度都会比较短,取模运算中只有低位参与散列;高位与低位进行异或,让高位也得以参与散列运算,使得散列更加均匀。

 

哈希函数的目标:

计算key在数组中的下标。

 

图解:

 

当有冲突时

第一个键值对A进来。通过计算其key的hash得到的index=0。记做:Entry[0] = A。 


第二个键值对B,通过计算其index也等于0, HashMap会将B.next =A,Entry[0] =B。


第三个键值对C,通过计算其index也等于0,那么C.next = B,Entry[0] = C。

 

这样我们发现index=0的地方事实上存取了A,B,C三个键值对(Entry),它们通过next这个属性链接在一起。我们可以将这个地方称为桶。 对于不同的元素,可能计算出了相同的函数值,这样就产生了“冲突”,这就需要解决冲突,“直接定址”与“解决冲突”是哈希表的两大特点。

 

 

下面我们继续putVal()方法

putVal()方法源码

/**
 * Implements Map.put and related methods
 *
 * @param hash hash for key
 * @param key the key
 * @param value the value to put
 * @param onlyIfAbsent if true, don't change existing value
 * @param evict if false, the table is in creation mode.
 * @return previous value, or null if none
 */
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {

    //tab是HashMap内部数组,n是数组的长度,i是要插入的下标,p数组中该下标对应的节点
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    
    
    //如果数组是null或者是空。则调用resize()方法进行扩容 首次put存值时这个条件会成立的
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;//扩容后的数组
    

    //使用位与运算代替取模得到下标对应的节点p 然后判断p是否为空 为空的话 也就是当前数组下标下没有节点
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);//创建Node对象
    

    //数组下标对应的节点不为空 也就是有值 
    else {
        Node<K,V> e; K k;


        //判断当前节点与要插入的key是否相同
        if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))
            e = p;

        // 判断该节点是否是树节点,如果是调用红黑树的方法进行插入
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {


            //普通链表插入
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);

                    
                    //长度大于等于8时转化为红黑树
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }

    
    //modCount数值+1表示结构改变
    ++modCount;


    //判断长度是否达到最大限度,如果是则进行扩容
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

参数说明:

参数 int hash:Key值通过hash()哈希函数计算得到的数组下标中的位置。

参数 K key:Key。

参数 V value:Value。

参数 boolean onlyIfAbsent:是否覆盖旧值,true表示不覆盖,false表示覆盖,默认为false。

参数 boolean evict:创建模式。一般不涉及。
 

putVal()方法几个要点

<1> 如果数组还没有初始化(数组长度是0),则先初始化。

<2> 通过hash方法计算key的hash值,进而计算得到应该放置到数组的位置。

<3> 如果该位置为空,则直接放置此处(包装成Node)

<4> 如果该位置不为空,而且元素是红黑树,则插入到其中。

<5> 如果是链表,则遍历链表,如果找到相等的元素则替换,否则插入到链表尾部。

<6> 如果链表的长度大于或等于8,则将链表转成红黑树。

 

 

源码分析之扩容

resize()方法源码

/**
 * 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() {

    //oldTab 原数组
    Node<K,V>[] oldTab = table;
    
    //原数组大小
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    

    //原数组最大容量
    int oldThr = threshold;

    //新数组大小 新数组容量
    int newCap, newThr = 0;


    //如果原数组长度大于0
    if (oldCap > 0) {

        //如果原数组长度超过了设置的最大长度(1<<30,也就是最大整型正数)
        if (oldCap >= MAXIMUM_CAPACITY) {
            // 直接把Integer阈值设置为原数组容量 然后返回原数组
            threshold = Integer.MAX_VALUE;
            return oldTab;
        }

        //否则 新数组的大小和容量变成原数组最大容量的2倍
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                 oldCap >= DEFAULT_INITIAL_CAPACITY)
            newThr = oldThr << 1; // double threshold
    }

    //如果原数组的最大容量大于0 也就是说 新建HashMap的时候指定了数组长度
    else if (oldThr > 0) // initial capacity was placed in threshold
         newCap = oldThr;
    else {               // zero initial threshold signifies using defaults
        
        //这种情况就是创建HashMap时候使用的是无参构造方法  新数组最大长度是默认16 最大容量是默认16*0.75=12
        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) {


        // 循环遍历原数组,并给每个节点计算新的位置
        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> 扩容时,如果创建HashMap时候,指定了大小。则扩容时新的数组使用传参的大小。

                     如果没有传参,则扩容时新的数组默认最大长度 16 默认最大容量12

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

  

static final float DEFAULT_LOAD_FACTOR = 0.75f;

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16


newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);

 

<3> 扩容源码可知

当链表的长度>=8且数组长度>=64时,会把链表转化成红黑树

当链表长度>=8,但数组长度<64时,会优先进行扩容,而不是转化成红黑树。

当红黑树节点数<=6,自动转化成链表。


红黑树在jdk1.8之后出现的,jdk1.7采用的是数组+链表模式

 

小结1

<1> HashMap采用链地址法,当发生冲突时会转化为链表,当链表过长会转化为红黑树提高效率。

<2> HashMap默认最大长度是16,默认加载因子是0.75,默认最大容量是16*0.75=12。

 

 

 

源码分析之get()方法

 

使用

HashMap<String, String> map = new HashMap<>(5);
map.get("2");

 

源码

public V get(Object key) {
    Node<K,V> e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}

 

getNode方法源码

/**
 * Implements Map.get and related methods
 *
 * @param hash hash for key
 * @param key the key
 * @return the node, or null if none
*/
final Node<K,V> getNode(int hash, Object key) {
    Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
    if ((tab = table) != null && (n = tab.length) > 0 &&
        (first = tab[(n - 1) & hash]) != null) {
        if (first.hash == hash && // always check first node
            ((k = first.key) == key || (key != null && key.equals(k))))
            return first;
        if ((e = first.next) != null) {
            if (first instanceof TreeNode)
                return ((TreeNode<K,V>)first).getTreeNode(hash, key);
            do {
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    return e;
            } while ((e = e.next) != null);
        }
    }
    return null;
}

 

 

 

 

 

 

 

 

四.线程安全

由源码可知,HashMap的get()方法和put()方法等等的方法。并没有同步。所以肯定不是线程安全的。

在多线程的情况下无法保证数据的一致性。举个例子:HashMap下标1的位置为null,线程A需要将节点X插入下标1的位置,在判断是否为null之后,线程被挂起;此时线程B把新的节点Y插入到下标1的位置;恢复线程A,节点X会直接插入到下标1,覆盖节点Y,导致数据丢失。

 

解决方法

<1> 采用Hashtable。

<2> 调用Collections.synchronizeMap()方法来让HashMap具有多线程能力。

<3> 采用ConcurrentHashMap。

前两个方案的思路是相似的,均是在每个方法中,对整个对象进行上锁。这样其实是会造成资源浪费的。以为比如线程A访问的是数组下标1的节点。线程B访问的是数组下标2的节点。两个线程是可以同时访问的。这种情况是没必要同步的。这也就是HashTable和Collections.synchronizeMap()方法两个方法的弊端。

而ConcurrentHashMap是在关键的地方加了同步。

https://blog.youkuaiyun.com/weixin_37730482/article/details/69281691

 

关于HashMap线程安全,还有一个fast-fail问题,即快速失败。当使用HashMap的迭代器遍历HashMap时,如果此时HashMap发生了结构性改变,如插入新数据、移除数据、扩容等,那么Iteractor会抛出fast-fail异常,防止出现并发异常,在一定限度上保证了线程安全。

 

源码

abstract class HashIterator {
    Node<K,V> next;        // next entry to return
    Node<K,V> current;     // current entry
    int expectedModCount;  // for fast-fail
    int index;             // current slot

    HashIterator() {
        expectedModCount = modCount;
        Node<K,V>[] t = table;
        current = next = null;
        index = 0;
        if (t != null && size > 0) { // advance to first entry
            do {} while (index < t.length && (next = t[index++]) == null);
        }
    }

    public final boolean hasNext() {
        return next != null;
    }

    final Node<K,V> nextNode() {
        Node<K,V>[] t;
        Node<K,V> e = next;
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        if (e == null)
            throw new NoSuchElementException();
        if ((next = (current = e).next) == null && (t = table) != null) {
            do {} while (index < t.length && (next = t[index++]) == null);
        }
        return e;
    }

    public final void remove() {
        Node<K,V> p = current;
        if (p == null)
            throw new IllegalStateException();
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        current = null;
        K key = p.key;
        removeNode(hash(key), key, null, false, false);
        expectedModCount = modCount;
    }
}



final class KeyIterator extends HashIterator
    implements Iterator<K> {
    public final K next() { return nextNode().key; }
}

    
final class ValueIterator extends HashIterator
    implements Iterator<V> {
    public final V next() { return nextNode().value; }
}

    
final class EntryIterator extends HashIterator
    implements Iterator<Map.Entry<K,V>> {
    public final Map.Entry<K,V> next() { return nextNode(); }
}

 

也就是说。HashIterator抽象类中nextNode()方法中modCount != expectedModCount是否相等。如果不相等抛出异常。而expectedModCount变量是在HashIterator抽象类的构造方法中初始化的 expectedModCount = modCount;

而每次HashMap有修改,比如put一个元素modCount会加一的。

 

下面三个类就是我们Iterator方式遍历HashMap时,用到的三个类。

比如

Set<String> set = hashMap.keySet();
Iterator<String> iterator = set.iterator();
   while (iterator.hasNext()) {
   String key = iterator.next();
   String value = hashMap.get(key);
   Log.i("TAG", "key----:" + key + "----value----:" + value);
}

 

fast-fail异常只能当做遍历时的一种安全保证,而不能当做多线程并发访问HashMap的手段。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值