Java容器之Map接口

Map接口和Collection接口没有任何关系,也是一个顶级接口

public interface Map<K,V> {}

其中,K:key,V:value。
定义map对象最好指定key和value对应的类型,key和value要求必须是复杂类型,不能采用简单类型。

map接口中有一个内部接口Entry,每个Entry对象用于封装一对key/value,value允许修改,但是key不允许修改。

interface Entry<K,V> {
	K getKey();
	V getValue();
	V setValue(V value);
}

Entry接口中还通过静态方法提供了一组比较器的默认实现,用于规范其中存放数据的规则。map中所有存储的数据都将封装为Entry对象,一个key/value对对应一个Entry对象在这里插入图片描述

 Map接口中的常见方法

  • Object put(Object key, Object value)用于存储一个键值对,如果出现key值冲突,则后盖前;允许key和value为null,但是key值只能有一个null,value没有null的个数限制。
  • int size()获取集合中元素Entry的个数。
  • Object remove(Object key)根据key值移除对应的key-value键值对,并且返回删除的value值。
  • Object get(Object key)按照key值获取对应的value值,如果key值不存在则返回null。
  • boolean containsKey(Object key)判断map中是否存在某个特定的key值。
  • boolean containsValue(Object value)判断map中是否存在某个特定的value值。
  • void clear() 清空当前map中的所有元素。
  • Set keySet()返回所有key所组成的Set集合,然后就可以通过key值获取对应的 value值。
  • Collection values()获取所有value值所构成的Collection集合。
  • Set entrySet()返回map中所存储的所有entry对象,一个 entry中保存一个key/value键值对。
  • forEach和lambda表达式实现map输出。

例:

Map<String,Integer> map=new HashMap<>(); //String--key,Integer--value
	for(int i=0;i<10;i++){
		map.put("key-"+i,i);
	//按照key值存储数据,key要求必须唯一,如果出现key值重复,则后盖前;value没有任何特殊要求
	}
	//判断map中的key知否有一个叫做key-9的键
	if(map.containsKey("key-9")){
		Integer value=map.get("key-9");//按照key值获取key所对应的value值
		System.out.println(value);
		map.remove("key-9"); //删除对应的key-5的value值
	}
	System.out.println(map.size());//获取map中存储的key-value对的数量

遍历访问

  1. 依赖key值进行遍历
    Set<String> keys=map.keySet();//获取map中所有的key所组成的Set集合
    	Iterator<String> it=keys.iterator();
    	while(it.hasNext()){
    		String key=it.next();
    		Integer value=map.get(key); //根据key获取key所对应的value值
    }
    
  2. 直接遍历所有的value值
    Collection<Integer> values=map.values(); //没有直接提供方法根据value获取对应的key,因为value值没有唯一性的约束
    values.forEach(System.out::println);
    
  3. 获取所有的entry,存储在map中的键值对都是封装为Entry对象,一个key-value对对应一个entry对象
//导入import java.util.Map.Entry;
Set<Entry<String,Integer>> sets=map.entrySet();
	for(Entry<String,Integer> tmp:sets){
	String key=tmp.getKey(); //获取一个key-value中的key值
	Integer value=tmp.getValue(); //获取一个key-value中的value值
}

Map的实现类

HashMap

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

如果不指定泛型,则默认key-value的类型都是Object
具体的内部数据存储方式

transient Node<K,V>[] table;
//哈希表的本质就是一个数组,数组中的每个元素称为一个桶,桶里存放的是一个key-value组成的链表或者红黑树

静态内部类Node用于实现了Entry接口,HashMap中存储的key-value对的数据被封装为Node对象,其中key就是存放的键值,用于决定具体的存放位置【桶的位置】;value就是具体存放的数据;hash就是当前Node对象的hash值缓存;next用于指向下一个Node节点【单向链表】

static class Node<K,V> implements Map.Entry<K,V> {
	final int hash;
	final K key;
	V value;
	Node<K,V> next;
}

在这里插入图片描述

阈值

static final int TREEIFY_THRESHOLD = 8;
 //树化阈值,即链表中的Node个数超过这个值时会自动转换为红黑树
 
static final int MIN_TREEIFY_CAPACITY = 64;
 //最小树形化容量阈值,当哈希表中的所有元素个数>该值时才允许进行树形化链表(即将链表转换为红黑树),否则桶内元素太多时,直接扩容,而不是树形化
 
static final int UNTREEIFY_THRESHOLD = 6;
//桶的链表还原阈值,就是红黑树转换为单向链表的阈值。当在扩容时,此时所有存储的元素都将重新计算所存放的桶的位置,重新计算后当原有的红黑树中的节点个数<6时,将红黑树转换为单向链表

 构造器

public HashMap() { 
//构建一个空的HashMap,默认使用初始化容积值16,默认负载因子值为0.75。当前Map集合中允许存放的元素个数为【初始化容积值*负载因子】,16*0.75=12,当集合中存放的key-value个数超过12时,会自动进行扩容处理


	this.loadFactor = DEFAULT_LOAD_FACTOR; 
// all other fields defaulted
}

HashMap的存储结构
具体存放数据采用的是Node[]数组,每个数组中的元素称为一个桶bucket,一个桶对应一个hash映射的值,例如0、1等,可能会出现不同的key,但是映射位置相同。

例如16和48映射的位置都是0【hash%16】。

所以采用单向链表存储hash映射值相同的所有数据。为了避免一个单向链过长的问题,所以JDK1.8引入了红黑树,当一个链表上的元素个数大于8时,会自动将链表(O(n))转换为红黑树,以提高检索效率(O(logN));当删除节点使某个红黑树上的节点个数小于阈值(6)时会自动将红黑树转换为单向链表。

  • capacity当前数组的容量,始终保持为2的n次方,可以扩容,每次扩容后数组的大小为当前数组的2倍。
  • loadFactor负载因子(加载因子),默认为0.75,主要是为了减少hash冲突。
  • threshold扩容阈值,等于capacity*loadFactor。如果当前集合中存储的元素个数大于阈值,会自动进行扩容处理。

添加元素的方法put

final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean
evict) {
	Node<K,V>[] tab; Node<K,V> p; int n, i;
	//1、判断table为空或者长度为0,即是否没有元素,如果没有元素则使用resize方法进行扩容。所谓的扩容处理就是将数组的长度加大一倍
	
	if ((tab = table) == null || (n = tab.length) == 0)
		n = (tab = resize()).length; //获取扩容后的新数组长度
		
//2、计算插入存储的数组索引i,
		if ((p = tab[i = (n - 1) & hash]) == null)
			tab[i] = newNode(hash, key, value, null);
		else {
			Node<K,V> e; K k;
			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);
						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;
		if (++size > threshold)
			resize();
		afterNodeInsertion(evict);
		return null;
	}


map的具体实现类:HashMap、TreeMap、LinkedHashMap、Hashtable等

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值