Java学习-05-韩顺平老师
Java-集合Map篇
目录:
01-Map接口
1.1-Map体系图
1.2-Map接口特点
1.3-setEntry、keySet、Values
1.4-常用方法
1.5-遍历方法
02-HashMap类
2.1-基本介绍
2.2-底层原理
03-Hashtable类
3.1-基本介绍
3.2-底层原理
04-HashMap vs Hashtable
05-Properties类
06-如果选择集合实现类
Map接口
Map体系继承图:
Map接口特点:
注意:这里将的是JDK8的Map接口特点。
1.Map和Collection并列存在。用于保存具有映射关系的数据:Key-Value。
2.Map中的key和value可以是任意引用类型的数据,会封装到HashMap$Node
对象中。
3.Map中的key不允许重复,其实和HashSet一样。
4.Map的Value可以重复。
5.Map中的key可以为null,value也可以为null,但是key为null,只能有一个。
6.常见的String类作为key。
7.key和value之间存在单向一对一关系,即通过指定的key总能获得value。
8.Map接口常用的实现类:HashMap、Hashtable 和 properties,LinkedHashMap。
entrySet和keySet和Values:
1.k-v 最后是存放在HashMap$Node数组中 Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
2.k-v 为了方便程序员遍历,还会创建 EnterySet集合,该集合存放的元素类型是Entry,而Entry对象
就有k,v EntrySet<Entry<K,V>>。
3.entrySet中定义的是Map.Entry,但实际上存储的还是HashMap$Node,这是因为HashMap$Node 实现了
Map.Entry接口
4.所以实际上在entrySet中,元素Entry存放的只是对 table[] 数组的引用,并没有生成新的集合,只是为
了遍历方便。
5.当把HashMap$Node对象存放到entrySet,之所以方便遍历是Map.Entry提供了获取键key和value的方法
k getKey() , v getValue() 后面在遍历里面演示。
6.还可以只获得所有的简直对象 KeySet对象实际上是一个Set集合,也可以直接获得值 Values实际上是Collection集合。
常用方法:
1.put:添加。
2.remove:根据键删除映射关系。
3.get:根据键获取值。
4.size:获取元素个数。
5.isEmpty:判断个数是否为0。
6.clear:清空。
7.containsKey:查找键是否存在。
演示代码:采用HashMap(向上转型)演示
public class Map_ {
public static void main(String[] args) {
Map map = new HashMap();
// put
map.put("张三",10);
map.put("李四",8);
map.put("王五",4);
System.out.println(map); // {李四=8, 张三=10, 王五=4}
// remove
map.remove("张三");
System.out.println(map); // {李四=8, 王五=4}
// get
System.out.println(map.get("李四")); // 8
// size
System.out.println(map.size()); // 2
// isEmpty
System.out.println(map.isEmpty()); // false
// clear
map.clear();
System.out.println(map); // {}
System.out.println(map.containsKey("张三")); // false
}
}
遍历方法: 只演示同时取出 key 和 value 的方法,单独取出 value 可以使用 map.Values。
- 第一种使用 keySet 分别采用 迭代器 和 增强for循环实现。
public class MapErgodic {
public static void main(String[] args) {
Map map = new HashMap();
map.put("张三",10);
map.put("李四",8);
map.put("王五",4);
Set keys = map.keySet(); // 返回的是一个Set接口对象
// 采用加强for来实现
for (Object key : keys) {
System.out.print( key + " " + map.get(key) + "\t"); // 李四 8 张三 10 王五 4
}
// 采用迭代器完成
Iterator iterator = keys.iterator(); // 获得迭代器
while (iterator.hasNext()) { // 判断是否有下一个
Object next = iterator.next();
System.out.print(next + " " + map.get(next) + "\t"); // 李四 8 张三 10 王五 4
}
}
}
- 第二种使用 entrySet提供的方法实现,同样采用 迭代器 和 增强for循环 实现。
public class MapErgodic {
public static void main(String[] args) {
Map map = new HashMap();
map.put("张三",10);
map.put("李四",8);
map.put("王五",4);
Set entrySet = map.entrySet(); // 获取一个Set对象,存储着多个entry
// 使用增强for循环
for (Object obj :entrySet) {
// 向下转型,因为Map.Entry才有 getKey 和 getValue 方法。
Map.Entry entry = (Map.Entry)obj;
System.out.print(entry.getKey() + " " + entry.getValue() + "\t"); // 李四 8 张三 10 王五 4
}
// 使用迭代器实现
Iterator iterator = entrySet.iterator();
while (iterator.hasNext()) {
// 同样是向下转型
Map.Entry entry = (Map.Entry)iterator.next();
System.out.print(entry.getKey() + " " + entry.getValue() + "\t"); // 李四 8 张三 10 王五 4
}
}
}
HashMap类
基本介绍:
1.HashMap是Map接口使用频率最高的实现类。
2.HashMap 是以键值对 key-val对的方式来存储数据[双列](HashMap$Node类型)
3.key 不能重复,但是值可以重复,允许使用null键和null值,但null键只能有一个。
4.如果添加的key相等,则会覆盖原来的key-val 里面的value。
5.和HashSet一样不保证映射关系,底层是以Hash表的方式存储,jdk8以后HashMap底层
是 数组+链表+红黑树。
6.HashMap没有实现同步,因此线程不安全的,方法没有做同步互斥操作,没有synchronied。
底层原理:
1.HashMap底层维护的是Node类型的数组table,默认初始化为null。
2.当创建对象时,将加载因子(loadfactor)初始化为0.75。
3.当添加key-val时,通过key的哈希值获得在table中的索引,让后判断索引处是否
有元素,如果没有元素直接添加。如果索引处有元素,继续判断索引处的key值是否
和要添加元素的key值相等,如果相等,就直接替换val值,如果不相等就循环判断
该索引处所有链表上有没有相等的key值,如果有就替换val,如果没有就在最后添
加该元素。
4.第一次添加则需要将table扩容,默认扩容到16个Node节点,临界值(threshold)12
5.以后再进行扩容,扩容机制与HashSet一致,可以具体查看上一篇文章,每次扩容到
原来的两倍,临界值也根据加载因子随之变化。
6.在java8中,如果一条链表的元素到达个个数超过了TREEIFY_THRESHOLD = 8,
并且table的大小MIN_TRREEIFY_CAPACITY = 64(默认),就会进行树化,否则
仍然采用数组扩容机制。
Hashtable类
基本介绍:
1.存放的元素还是键值对 key-val
2.Hashtable的键值都不能为空。
3.Hashtable使用的方法基本上和HashMap一致。
4.Hashtable是线程安全的,实现了方法同步和互斥。
底层原理:
1.底层有数组Hashtable$Entry[] 初始化值为11
2.临界值 threshold 8 = 11 *0.75。
3.扩容: 一旦元素的个数超过临界值,就进行扩容,扩容到原来的2倍加1,
例如第一次扩容为 2 * 11 + 1 = 23,临界值为 23*0.75=17。
4.执行add 时 实际上是执行了 addEntry(hash,key,value,index),封装成Entry对象。
HashMap vs Hashtable:
线程安全:HashMap 线程不安全,Hashtable线程安全。
效率:HashMap 高 ,Hashtable 较低。
允许null键null值: HashMap允许,Hashtable 不允许。
Properties类
基本介绍:
1.Properties类继承自Hashtable类并实现了接口Map,也是使用键值对的形式存储。
2.他的特点与Hashtable类似。
3.Properties还可以用于从 xxx.properties文件中,加载数据到Properties类对象
并进行读取和修改。
如果选择集合实现类:
1.判断储存类型(一组对象还是一组键值对):
一组对象:Collection接口(判断允许重复还是不允许重复)
允许重复:List
增删多:LinkedArrayList(底层维护一个双向链表)
查改多:ArrayList(底层维护一个Object类型的可变数组)
不允许重复:Set
无序:HashSet(底层是HashMap,维护一个哈希表,即数组 + 链表 + 红黑树)
有序:TreeSet(底层是TreeMap实现了排序功能,可以自定义排序规则)
一组键值对:Map
键无序:HashMap(维护一个哈希表,jdk7:数组+链表,jdk8:数组 + 链表 + 红黑树)
键有序:TreeMap(通过算法实现插入时排序,通过传入一个Comparator匿名内部类对象来实现自定义排序)
键插入和取出顺序一致:LinkedHashMap
读取文件:Properties