复习12:Map

集合:Map

Map是什么

特殊的集合接口

Map的特点

  • Map中的元素被称为键值对(key-value),一个key对应一个value,例如电话簿中每一个名字对应一个电话号码(key不可重复,value可重复)

    • key:Set组成
    • value:List组成
  • 底层实现是散列表,即数组+链表(List+Set)+[树]

Map的实现类和子接口

  • HashMap:线程不安全,无序,key不可重复(不可重复的原理与HashSet相同)
    • Hashtable:线程安全,类似StringBuffer、Vector
  • SortedMap
    • TreeMap:可排序(原理与TreeSet相同),对key进行排序
  • LinkedHashMap

TreeMap排序时,若选择传入构造器Comparator,则构造器的泛型类型必须与key的泛型类型一致

Map的迭代

  • Map不能使用迭代器(没有实现Iterable接口)
  • **!!!**遍历key:Set keySet()
package demo;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Demo1<E> {
	public static void main(String[] args) {
		Map<String,String> map = new HashMap<String,String>();
		map.put("1","a");
		map.put("2","b");
		map.put("3","c");
		map.put("4","d");
		//返回key的set集合
		Set<String> keys = map.keySet();
		for(String set : keys) {
			System.out.println(set+"="+map.get(set));
		}
	}
}
  • 遍历value:Collection values()
package demo;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class Demo1<E> {
	public static void main(String[] args) {
		Map<String,String> map = new HashMap<String,String>();
		map.put("1","a");
		map.put("2","b");
		map.put("3","c");
		map.put("4","d");
		
		Collection<String> values = map.values();
		for(String str : values) {
			System.out.println(str);
		}
	}
}
 

无法通过value得到key,所以这种方式的遍历只能获得Map的value

  • **!!!**遍历key-value:Set< Entry<k,v> > entrySet()
package demo;

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class Demo1<E> {
	public static void main(String[] args) {
		Map<String,String> map = new HashMap<String,String>();
		map.put("1","a");
		map.put("2","b");
		map.put("3","c");
		map.put("4","d");
		
		Set<Entry<String,String>> entrys = map.entrySet();
		for(Entry<String,String> entry : entrys) {
			System.out.println(entry);
		}
		
	}
}
 

Entry<k,v>是Map的一个内部类

Map的API

  • put(key,value):添加新的键值对(hashCode()根据key计算位置)
    • 可能会出现的两种情况:
      • key不同,hashCode()的计算结果相同:链表方式存储,原来的元素向后推
      • key值相同:新的键值对覆盖原来的键值对

注意1(重要):key和value可以为null

注意2:HashSet:新元素不会替换旧元素

  • get(key):通过key获得对应的value
  • remove(key):通过key删除整个键值对,返回被删除的value
    • 如果查询不到键值对,返回null
  • contains…(…):
    • containsKey(key)
    • containsValue(value)
  • isEmpty()
  • putAll(Map map)

HashMap

HashMap的底层实现

数组+单向链表+红黑树

  • 当链表中的数量超过8个时,链表转换为红黑树,所以如果哈希冲突(不同元素的hashCode()得到相同的结果)多的话,数组的元素将会是红黑树
HashMap的长度与扩容方式
  • HashMap的初始长度:16
  • HashMap的加载因子:0.75
    • 加载因子:当Map中的键值对数量达到长度的0.75时,Map自动扩容(Hashtable相同)
  • HashMap的扩容方式:old*2
    • 例如,Map的初始长度为16,当Map中的键值对个数达到12个时,Map的长度自动扩容为32

选择加载因子为0.75的原因:

  • HashMap扩容时,键值对的位置都要进行重新计算
  • 新插入的键值对的位置也要进行计算,当剩余空间太小时,计算所需时间会变长

综合考虑以上两点,当加载因子为0.75时,效率比较高

  • Hashtable的初始长度:11

  • Hashtable的加载因子:0.75

  • Hashtable的扩容方式:old*2+1

Set的初始长度、加载因子、扩容方式都与Map相同

LinkedHashMap

LinkedHashMap是什么

LinkedList与HashMap的结合,底层仍然是HashMap

LinkedHashMap的实现

双向链表+散列表

LinkedHashMap的特点
  • key不可重复
  • 有序(有序指元素的顺序与添加的先后顺序一致,并非是排序)
  • 节点多了before和after属性(对应LinkedList的provious和next)

Map实现类的对比

HashMap Hashtable LinkedHashMap TreeMap
元素排序 无序 无序 加入顺序 字典顺序
元素 key和value可以为null 不接受null key和value可以为null 仅接受value为null
底层实现 数组+单向链表+红黑树 数组+链表 散列表+双向链表 红黑树
线程安全 非synchronized synchronized 非synchronized 非synchronized
线程同步 不同步 同步 不同步 不同步
效率
默认长度 16 11
加载因子 0.75 0.75
扩容方式 old*2 old*2+1

扩展:ConcurrentHashMap

出现原因

HashMap线程不安全,Hashtable性能不好

底层实现

由16个segment组成,每个sgment都是线程安全的HashMap(锁分段技术)

  • 将数据分成一段一段(segment)地存储,然后给每个数据段配一把锁,当一个线程占用锁访问其中某个数据段时,其它数据段的数据也能被其它线程访问

上述实现是基于JDK1.7,在JDK1.8中ConcurrentHashMap完全是在HashMap的基础上进行加锁

特点
  • 对segment的改写操作,不影响其它segment的使用
    • 理想情况下,最多支持6个线程并发修改segment,这16个线程分别访问不同的segment
  • segment加锁时,所以读线程是不会受到阻塞的
使用场景

高并发

方法的实现原理
  • get():不加锁,先定位到segment,然后再找到头结点进行读取操作。因为value是volatile变量,所以可以保证在竞争条件时读取到最新的值,如果读到的value是null,则数据可能正在被修改,那么就调用方法ReadValueUnderLock(),加锁保证读到的数据是正确的。
  • Put():加锁,一律添加到hash链的头部
  • Remove():加锁,由于next是final类型不可改变,所以必须把删除的节点之前的节点都复制一遍。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章