一、基本介绍:
1、Map接口实现类的特点[很实用]
注意:这里讲的是JDK8的Map接口特点
1)Map与Collection并列存在。用于保存具有映射关系的数据:Key-Value
2)Map 中的 key和value 可以是任何引用类型的数据,会封装到HashMap$Node 对象中
3) Map 中的 key 不允许重复,原因和HashSet 一样,如果有重复的,后面的key的value会把前面的相同的key的value替换掉
4) Map 中的 value 可以重复
5) Map 的key 可以为 null,value 也可以为null ,注意 key 为null, 只能有一个, value 为null,可以多个.
6)常用String类作为Map的 key,即key不一定得是String
7)key和value之间存在单向一对一关系,即通过指定的 key 总能找到对应的 value,使用get()方法
- package Collection_;
-
- import java.util.HashMap;
- import java.util.Map;
-
- @SuppressWarnings({"all"})
- public class Map_ {
- public static void main(String[] args) {
- Map map=new HashMap();
- map.put("no1","韩顺平");
- map.put("no2","张无忌");
-
- map.put("no1","张三丰");//当有相同的k,就等价于替换
- map.put("no3","张三丰");
-
- map.put(null,null);
- map.put(null,"abc");//替换上面的null
-
- map.put("no4",null);
- map.put("no5",null);
-
- map.put(1,"赵敏");
- map.put(new Object(),"金毛狮王");
-
- //通过get()方法,传入key,会返回对应的value
- System.out.println(map.get("no2"));
- System.out.println("map="+map);
- }
- }
- //张无忌
- //map={no2=张无忌, null=abc, no1=张三丰, 1=赵敏, no4=null, no3=张三丰, no5=null, java.lang.Object@7ef20235=金毛狮王}
- //输出的顺序与hash值有关,所以是无序的
2、Map接口的特点:(看源码)
(1)Map存放数据,一对k-v是放在一个HashMap$Node中的,有因为Node实现了Entry接口,有些书上也说,一对k-v就是一个Entry
3、Map接口的常用方法
4、Map接口的遍历方法:
二、HashMap
- package Collection_;
-
- import java.util.*;
-
- @SuppressWarnings({"all"})
- public class MapFor {
- public static void main(String[] args) {
- Map map=new HashMap();
- map.put("邓超","孙俪");
- map.put("王宝强","马蓉");
- map.put("宋喆","马蓉");
- map.put("刘令博",null);
- map.put(null,"刘亦菲");
- map.put("鹿晗","关晓彤");
-
- //第一组:先取出所有的Key,通过Key取出对应的Value
- Set keyset=map.keySet();
- //(1)增强for
- System.out.println("------------1.1增强for----------");
- for (Object key :keyset) {
- System.out.println(key+"-"+map.get(key));
- }
- //(2)迭代器
- System.out.println("------------1.2迭代器----------");
- Iterator iterator=keyset.iterator();
- while (iterator.hasNext()) {
- Object key = iterator.next();
- System.out.println(key+"-"+map.get(key));
- }
-
- //第二组:把所有的values取出
- Collection values=map.values();
- //这里可以使用所有的Collections使用的遍历方法
- //(1)增强for
- System.out.println("------------2.1增强for----------");
- for (Object value :values) {
- System.out.println(value);
- }
- //(2)迭代器
- System.out.println("------------2.2迭代器----------");
- Iterator iterator1=values.iterator();
- while (iterator1.hasNext()) {
- Object value = iterator1.next();
- System.out.println(value);
- }
-
- //第三组:EntrySet
- Set entrySet=map.entrySet();
- System.out.println("------------3.1增强for----------");
- for (Object entry :entrySet) {
- Map.Entry m=(Map.Entry)entry;//将entry向下转型成Map.Entry
- System.out.println(m.getKey()+"-"+m.getValue());
- }
- System.out.println("------------3.2迭代器----------");
- Iterator iterator2=entrySet.iterator();
- while (iterator2.hasNext()) {
- Object entry = iterator2.next();
- //System.out.println(next.getClass());//HashMap$Node-->实现-->Map.Entry(getKey,getValue);
- Map.Entry m=(Map.Entry) entry;//向下转型成Map.Entry
- System.out.println(m.getKey()+"-"+m.getValue());
- }
- }
- }
- //------------1.1增强for----------
- //邓超-孙俪
- //宋喆-马蓉
- //刘令博-null
- //null-刘亦菲
- //王宝强-马蓉
- //鹿晗-关晓彤
- //------------1.2迭代器----------
- //邓超-孙俪
- //宋喆-马蓉
- //刘令博-null
- //null-刘亦菲
- //王宝强-马蓉
- //鹿晗-关晓彤
- //------------2.1增强for----------
- //孙俪
- //马蓉
- //null
- //刘亦菲
- //马蓉
- //关晓彤
- //------------2.2迭代器----------
- //孙俪
- //马蓉
- //null
- //刘亦菲
- //马蓉
- //关晓彤
- //------------3.1增强for----------
- //邓超-孙俪
- //宋喆-马蓉
- //刘令博-null
- //null-刘亦菲
- //王宝强-马蓉
- //鹿晗-关晓彤
- //------------3.2迭代器----------
- //邓超-孙俪
- //宋喆-马蓉
- //刘令博-null
- //null-刘亦菲
- //王宝强-马蓉
- //鹿晗-关晓彤
1、练习题:
//我的代码:
- package Collection_;
-
- import java.util.*;
-
- @SuppressWarnings({"all"})
- public class MapExercise {
- public static void main(String[] args) {
- Map map=new HashMap();
- map.put(1234,new Employee("jack",10000,"1234"));
- map.put(1235,new Employee("tom",20000,"1235"));
- map.put(1236,new Employee("smith",30000,"1236"));
- System.out.println("========第一种遍历方式========");
- Set keyset=map.keySet();
- System.out.println("========增强for循环========");
- for (Object key :keyset) {
- Employee employee=(Employee) map.get(key);
- if(employee.getSal()>18000) {
- System.out.println(employee);
- }
- }
- System.out.println("========迭代器========");
- Iterator iterator=keyset.iterator();
- while (iterator.hasNext()) {
- Object key = iterator.next();
- Employee employee=(Employee) map.get(key);
- if(employee.getSal()>18000) {
- System.out.println(employee);
- }
- }
- System.out.println("========第二种遍历方式========");
- Set entrySet=map.entrySet();
- System.out.println("========增强for循环========");
- for (Object entry :entrySet) {
- Map.Entry m=(Map.Entry) entry;
- Employee employee=(Employee) m.getValue();
- if(employee.getSal()>18000) {
- System.out.println(employee);
- }
- }
- System.out.println("========迭代器========");
- Iterator iterator1=entrySet.iterator();
- while (iterator1.hasNext()) {
- Object entry = iterator1.next();
- Map.Entry m=(Map.Entry) entry;
- Employee employee=(Employee) m.getValue();
- if(employee.getSal()>18000) {
- System.out.println(employee);
- }
- }
-
- }
- }
- class Employee{
- private String name;
- private double sal;
- private String id;
-
- public Employee(String name, double sal, String id) {
- this.name = name;
- this.sal = sal;
- this.id = id;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public double getSal() {
- return sal;
- }
-
- public void setSal(double sal) {
- this.sal = sal;
- }
-
- public String getId() {
- return id;
- }
-
- public void setId(String id) {
- this.id = id;
- }
-
- @Override
- public String toString() {
- return "Employee{" +
- "name='" + name + '\'' +
- ", sal=" + sal +
- ", id='" + id + '\'' +
- '}';
- }
- }
- //========第一种遍历方式========
- //========增强for循环========
- //Employee{name='tom', sal=20000.0, id='1235'}
- //Employee{name='smith', sal=30000.0, id='1236'}
- //========迭代器========
- //Employee{name='tom', sal=20000.0, id='1235'}
- //Employee{name='smith', sal=30000.0, id='1236'}
- //========第二种遍历方式========
- //========增强for循环========
- //Employee{name='tom', sal=20000.0, id='1235'}
- //Employee{name='smith', sal=30000.0, id='1236'}
- //========迭代器========
- //Employee{name='tom', sal=20000.0, id='1235'}
- //Employee{name='smith', sal=30000.0, id='1236'}
2、HashMap小结:
1)Map接口的常用实现类:HashMap、Hashtable和Properties。
2)HashMap是Map 接口使用频率最高的实现类。
3)HashMap 是以 key-val 对的方式来存储数据(HashMap$Node类型)
4) key 不能重复,但是是值可以重复,允许使用null键和null值。
5)如果添加相同的key,则会覆盖原来的key—val,等同于修改.(key不会替换,val会替换)
6)与HashSet一样,不保证映射的顺序,因为底层是以hash表的方式来存储的.
7)HashMap没有实现同步,因此是线程不安全的,方法没有做同步互斥的操作,没有synchronized
3、HashMap底层机制及源码剖析:
(1)扩容机制[和HashSet相同]
1)HashMap底层维护了Node类型的数组table,默认为null
2)当创建对象时,将加载因子(loadfactor)初始化为0.75.
3)当添加key-val时,通过key的哈希值得到在table的索引。然后判断该索引处是否有元素,如果没有元素直接添加。如果该索引处有元素,继续判断元素的key是否和准备加入的key相等,如果相等,则直接替换val;如果不相等需要判断是树结构还是链表结构,做出相应处理。如果添加时发现容量不够,则需要扩容
4)第1次添加,则需要扩容table容量为16,临界值(threshold)为12(16*0.75)
5)以后再扩容,则需要扩容table容量为原来的2倍,临界值为原来的2倍,即24,依次类推
6)在Java8中,如果一条链表的元素个数超过TREEIFYTHRESHOLD(默认是8),并且table的大小>=MINTREEIFYCAPACITY(默认64),就会进行树化(红黑树)
- //解读源码:
- package Collection_;
-
- import java.util.Comparator;
- import java.util.TreeMap;
- @SuppressWarnings({"all"})
- public class TreeMap_ {
- public static void main(String[] args) {
- // TreeMap treeMap=new TreeMap();
- // treeMap.put("dddjack","杰克");
- // treeMap.put("aaatom","汤姆");
- // treeMap.put("ccckristina","克瑞斯提诺");
- // treeMap.put("bbbsmith","史密斯");
- //
- // System.out.println("treemap="+treeMap);
- //输出:treemap={aaatom=汤姆, bbbsmith=史密斯, ccckristina=克瑞斯提诺, dddjack=杰克}
- //使用默认构造器,则是按首字母前后排序
-
- TreeMap treeMap=new TreeMap(new Comparator() {
- @Override
- public int compare(Object o1, Object o2) {
- //按照传入的k(String)的大小进行排序
- return ((String)o1).compareTo((String)o2);//从小到大
-
- //return ((String)o2).compareTo((String)o1);
- //从大到小
- //输出:treemap={dddjack=杰克, ccckristina=克瑞斯提诺, bbbsmith=史密斯, aaatom=汤姆}
-
- //return ((String)o1).length()-((String)o2).length();
- //按照传入的k(String)的长度大小进行排序
- //输出:treemap={aaatom=汤姆, dddjack=杰克, bbbsmith=史密斯, ccckristina=克瑞斯提诺}
- //注意:如果要添加的key的长度和已有的某个key的长度一样,那这个key就添加不进去,
- //cmp = cpr.compare(key, t.key);
- // else {
- // V oldValue = t.value;
- // if (replaceOld || oldValue == null) {
- // t.value = value;
- // }
- // return oldValue;
- // }
- //但是它的值会替换已有的那个key的值
- }
- });
- treeMap.put("dddjack","杰克");
- treeMap.put("aaatom","汤姆");
- treeMap.put("ccckristina","克瑞斯提诺");
- treeMap.put("bbbsmith","史密斯");
- System.out.println("treemap="+treeMap);
- //输出:treemap={aaatom=汤姆, bbbsmith=史密斯, ccckristina=克瑞斯提诺, dddjack=杰克}
- //跟使用默认情况时一样,说明默认情况下的底层机制即为这个
- }
- }
- /*
- 源码剖析:
- //1、构造器,把传入的实现了Comparator接口的匿名内部(对象),传给TreeMap的comparator
- public TreeMap(Comparator super K> comparator) {
- this.comparator = comparator;
- }
- //2、调用put方法
- //2.1 第一次添加,把k-v封装到Entry对象,放入root
- Entry
t = root; - if (t == null) {
- addEntryToEmptyMap(key, value);
- return null;
- }
- //2.2
- Comparator super K> cpr = comparator;
- if (cpr != null) {
- do {//遍历所有的key,给当前的key找到适当的位置,
- parent = t;
- cmp = cpr.compare(key, t.key);//动态绑定到我们的匿名内部类的compare()方法
- if (cmp < 0)
- t = t.left;
- else if (cmp > 0)
- t = t.right;
- else {//如果遍历过程中,发现准备添加的key和当前已有的key相等,就不添加了
- V oldValue = t.value;
- if (replaceOld || oldValue == null) {
- t.value = value;
- }
- return oldValue;
- }
- } while (t != null);
- }
- */