昨天写了Java集合中的Collection接口,今天继续写Map接口,记得要对之前的知识点做好巩固与复习的哦!!!
Map接口
Map接口储存一组成对的键-值对象,提供key(键)到value(值)的映射,Map中的key不要求有序,不允许重复。value同样不要求有序,但可以重复。最常见的Map实现类是HashMap,他的储存方式是哈希表,优点是查询指定元素效率高。
Map接口提供了将键映射到集合的对象,一个映射不能包含重复的键.
每个键最多只能映射到一个值.Map接口中同样提供了集合的常用方法,如clear()方法,isEmpty()方法,Size()方法等。
Map接口中有一个内部接口Entry,每个Entry对象用于封装一对key/value,value允许修改,但是key不允许修改。
interface Entry<K,V>{
K getKey();
V getValue();
V setValue(V value);
}
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-5的键
if(map.containsKey("key-5")){
Integer value=map.get("key-5");//按照key值获取key所对应的value值
System.out.println(value); map.remove("key-5"); //删除对应的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、Hashtable、TreeMap、LinkedHashMap)
1、HashMap是Map接口的实现类。
public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable
具体的内部数据存储方式:
transient Node<K,V>[] table;
//哈希表的本质就是一个数组,数组中的每个元素称为一个桶,桶里存放的是一个key-value组成的链表或者红黑树
补充:哈希表是数组的一种扩展,底层依赖数组支持按照下标快速访问元素的特性,可以通过hash函数将元素的键值映射为下标,然后将对应下标的数据存储在数组中的对应位置。当按照键值查询元素时,使用相同的hash函数将key转换为数组下标,从数组中按照下标对应的位置获取数据。
静态内部类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;
}
HashMap采用的是链表法解决哈希冲突问题,同时引入红黑树可以避免单个链表长度过长的问题 hash函数的涉及需要考虑简单高效和分布均匀两个方面,所以首先获取对象的hashCode值,然后要将hash值的高位和低位进行与运算后再针对数组长度进行求余。
HashMap线程不安全,进行多线程操作时可能会出现扩容时执行rehash操作的死循环问题、脏读问题和size值不精确的问题。
2、Hashtable是Map接口的实现类。
继承的是字典类,而不是AbstractMap,目前已经不再建议使用了。
public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, java.io.Serializable
hashtable的绝大多数方法上都有同步约束,所以线程安全的,但是hashtable线程安全的策略实现代价太大了。get和put所有相关操作都是synchronized的,相当于给整个hashtable添加一个所,多线程并发时只能有一个线程访问,其它线程只能阻塞等待,相当于将所有操作串行化,性能非常差
存储数据:使用Entry数组,hashmap采用的是Node数组
private transient Entry<?,?>[] table;
hashtable也是一个散列表,存储内容是key-value映射,通过链地址法实现的哈希表hashtable中不允许null值的key和value。
注意:在Hashtable的类注释中可以看到,Hashtable是保留类,已经不建议使用;推荐在单线程环境下使用HashMap替代,如果需要多线程使用ConcurrentHashMap。
3、TreeMap是Map接口的实现类。
TreeMap用于实现一个key-value的有序集合,是通过红黑树实现的。该Map根据key的自然顺序进行排序【Comparable接口】,同时允许根据创建Map时提供的Comparator比较器进行排序,具体使用取决于所使用的构造器。
public class TreeMap<K,V> extends AbstractMap<K,V> implements NavigableMap<K,V>, Cloneable, java.io.Serializable {
private transient Entry<K,V> root; //具体存放数据
private final Comparator<? super K> comparator;//排序的比较器
private transient int size = 0; //元素个数
private transient int modCount = 0; //修改次数,为了实现fail-fast
}
类定义:
继承于AbstractMap同时实现了Map接口,所以是一个Map类型,其中存储key-value对
实现了NavigatableMap接口,意味着支持一系列的导航方法,比如获取有序的key集合
实现了Cloneable接口,意味着支持浅克隆操作
实现了Serializable接口,意味着支持序列化和反序列化操作
节点定义:
static final class Entry<K,V> implements Map.Entry<K,V> {
K key; //节点中存储的key值
V value;//节点中存储的对应key的value值
Entry<K,V> left; //左子树
Entry<K,V> right; //右子树
Entry<K,V> parent; //当前节点的父节点
boolean color = BLACK; //节点的颜色,红黑树
}
常用方法:
put(K key, V value)用于添加一个Entry节点,其中key为指定的value所关联的键,value为指定key 对应的值,如果key值已经存在,则将key对应的value修改为新值
V get(K key)用于获取对应key的value值
特殊方法:
firstEntry返回TreeMap中最小的key
higherEntry(K key)获取位于key后一位的key-value
lowerEntry(K key)获取位于指定key前一个的key-value
HashMap通过hashCode值对其内容进行快速查找,而TreeMap中的所有元素会保持其固定的顺序。如果需要得到一个有序的结果集则应该使用TreeMap,注意:key需要实现Comparable接口或者创建TreeMap时需要指定实现Comparator接口的比较器。
HashMap通常比TreeMap效率要高一些,HashMap采用的是hash表,而TreeMap采用的是红黑树。建议多使用HashMap,只有在需要排序时才使用TreeMap。
4、LinkedHashMap是Map接口的实现类。
LinkedHashMap是HashMap的子类,LinkedHashMap额外维护了一个运行于所有条目的双向链表,默 认存储的是数据的插入顺序。HashMap是无序的,如果需要记录顺序的方式存取key-value可以使用LinkedHashMap 。
Map<Integer, String> map = new LinkedHashMap<>();
Random r = new Random();
for (int i = 0; i < 10; i++)
map.put(r.nextInt(100), i + "-value");
System.out.println(map);
Map<Integer,String> map2=new HashMap<>();
for (int i = 0; i < 10; i++)
map2.put(r.nextInt(100), i + "-value");
System.out.println(map2);
特殊构造器:
设置采用访问顺序:如果一个key被访问过则自动调整位置到默认位置的末尾,如果不进行访问则插入顺序一致。
Map<Integer,String> map=new LinkedHashMap(10,0.75f,true);
Random r=new Random();
map.put(111,"value1");
for(int i=0;i<10;i++)
map.put(r.nextInt(100),i+"-value");
System.out.println(map.get(111));//如果访问过111则自动排到双向链的末尾 System.out.println(map);
总结:
HashMap存取的顺序不一致,无序性,key值对象hashCode和equals方法(equals为true,要求hashCode值必须相等);
Hashtable底层采用数组+链表的方式实现,线程安全
LinkedHashMap是HashMap的子类,可以2种遍历顺序可以选择,是有序的,在HashMap存储数 据的基础上引入了双向链表,通过链表可以记录顺序(自定义缓存,会增加额外的时间和空间开销)
TreeMap底层采用红黑树,key要求Comparable接口或者Comparator接口的实现(HashMap和LinkedHashMap存储速度优于TreeMap,完成所有元素的添加操作后排序则TreeMap明显优于HashMap,key要求Comparable或者构建TreeMap对象时指定Comparator,equals和compareTo)
好了,今天就先总结学习到这里吧,要记得多多学习巩固,还要多理解一些其他相关知识点,让我们一起加油!!!