Map接口
![map目录](https://img-blog.csdnimg.cn/7fcbdfe90e374cf6a1c99f0ce3e54be5.png)
Map接口实现类特点(很实用)
- 和Collection并列存在,用于保存具有映射关系的数据:key-value
- Map中的key和value可以是任意引用类型的数据,会封装到
HashMap$Node
中 - Map中的key不可以重复,当有相同的key时,等价于替换value值
- Map中的value可以重复
- key和value都可以为null,但key为null只能有一个,value为null可以有多个
- 常用String类作为map的key
- key和value存在单向一对一的关系,即通过指定的key总能找到对应的value
- 一对key-value就是放在一个Node中的,Node实现了Entry接口,所以一对k-v也是一对Entry。
Map源码解读
- HashMap$Node node = newNode(hash, key, value, null);
- 为了方便程序员遍历,还会创建
EntrySet
集合,该集合存放元素的类型Entry,而一个Entry对象就有k,v。EntrySet<Entry<K,V>>
- EntrySet中定义的类型是Map.Entry,但实际存放的是HashMap$Node
- Map.Entry提供重要方法:getKey();getValue()
Map接口常用方法
- put添加
- remove根据键删除映射关系
- get根据键获取值
- size获取元素个数
- isEmpty判断元素个数为0
- clear清除
- containsKey查找键是否存在
Map的遍历方式
Map map = new HashMap();
map.put("赵敏", "张无忌");
map.put("金美英王", "白眉鹰王");
map.put("江直树", "袁湘琴");
map.put("迪丽热巴", "龚俊");
Set keySet = map.keySet();
System.out.println("=====第一种遍历方式======");
for (Object ob : keySet) {
System.out.println(ob + "-" + map.get(ob));
}
System.out.println("=====第二种遍历方式======");
Iterator iterator = keySet.iterator();
while (iterator.hasNext()) {
Object ob = iterator.next();
System.out.println(ob + "-" + map.get(ob));
}
System.out.println("=====第三种遍历方式======");
Collection values = map.values();
for (Object ob : values) {
System.out.println(ob);
}
System.out.println("=====第四种遍历方式======");
Set entrySet = map.entrySet();
for (Object ob : entrySet) {
Map.Entry entry = (Map.Entry) ob;
System.out.println(entry.getKey() + "-" + entry.getValue());
}
System.out.println("=====第五种遍历方式=====");
Iterator iterator1 = entrySet.iterator();
while (iterator1.hasNext()) {
Object ob = iterator1.next();
Map.Entry entry = (Map.Entry) ob;
System.out.println(entry.getKey() + "-" + entry.getValue());
}
Map接口实现类HashMap
- HashMap是Map接口使用频率最高的实现类。
- 与HashSet一样不保证映射的顺序。因为底层是以hash表的方式存储的。
- HashMap没有实现同步,因此是线程不安全的。
HashMap底层实现机制(和HashSet相同)
- 底层维护了
HashMap$Node类型的数组table
,默认为null - 创建对象时,将加载因子
loadfactor初始化为0.75
- 添加key-val时,通过key的哈希值得到在table上的索引。然后判断索引处是否有元素,如果没有元素直接添加;如果有元素,继续判断该元素的key和准备加入的key是否相等,如果相等直接替换;如果不相等,需要判断是树结构还是链表结构,需要做出相应处理。如果添加时发现容量不够,则需要扩容。
- 第一次扩容,需要
table容量为16,临界值为12
(16*0.75) - 之后再扩容,
table容量为原先2倍,临界值为24
。依此类推 - Java8中,
如果一条链表的元素个数超过TREEIFY_THRESHOLD(默认是8),并且table大小>=MIN_TREEIFY_CAPACITY(默认64),就会进行树化(红黑树)
。
Hashtable
- 存放的元素是键值对:K-V
- Hashtable的键值都不能为null,否则会抛出NullPointerException
- Hashtable使用方法基本和HashMap一样
- Hashtable是线程安全的,HashMap是线程不安全的。
Hashtable底层实现机制
- 底层有数组Hashtable$Entry[],初始化大小为11
- 临界值 threshold 8 = 11 * 0.75
- 扩容:当count>=threshold时就进行扩容
- 按照 int newCapacity = (oldCapacity << 1) + 1的大小进行扩容
Properties(Hashtable的子类)
- 继承自Hashtable类并实现Map接口,也是一种使用键值对的形式保存数据
- 使用特点和Hashtable类似
- Properties还可以用于从xxx.properties文件中,加载数据到properties类对象,并进行读取和修改
- 说明:工作后,xxx.properties文件通常作为配置文件
总结(开发中如何选择集合实现类)
主要取决于业务操作特点,然后根据集合类特性进行选择,分析如下:
- 先判断存储的类型(一组单列对象或一组双列键值对)
- 一组单列对象:Collection接口
- 允许重复:List
- 增删多:LinkedList(底层维护了一个双向链表)
- 改查多:ArrayList(底层维护Object类型的可变数组)
- 不允许重复:Set
- 无序:HashSet(底层是哈希表,jdk7是数组+链表,jdk8是数组+链表+红黑树)
- 排序:TreeSet
- 插入和取出顺序一致:LinkedHashSet(数组+双向链表)
- 一组双列键值对:Map
- 键无序:HashMap(底层是哈希表,jdk7是数组+链表,jdk8是数组+链表+红黑树)
- 键排序:TreeMap
- 键插入和取出顺序一致:LinkedHashMap
- 读取文件:Properties
其他知识点
- transient关键字用于,当类的某些属性需要序列化,有些不需要序列化时,只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。