Map接口的用法:
Map接口简介:Map接口是一种双列集合,它的每个元素都包含一个键对象Key和值对象Value,键和值对象之间存在一种对应关系,称为映射。从Map集合中访问元素时,只要指定了Key,就能找到对应的Value。格式如下:
HashMap<K,V>
k:指定的key值,类似于主键值,不能相同;
v:为map的数据值,可以相同,可以重复;
Map 接口的实现类有HashMap、TreeMap、HashTable、Properties等。
HashMap集合与HashTable集合:
- HashMap集合是Map接口的一个实现类,它用于存储键值映射关系,但必须保证不出现重复的键。它是线程不安全的,但是效率高;可以存储null的key值和value值。而且HashMap添加的数据是按照数据的HashCode来进行排序的,并不是按照添加顺序进行排序的。其底层为:数组+链表+红黑树
HashMap的常用方法:
Map m = new HashMap();
m.put("AA",111);
m.put("BB",222);
m.put("CC",333);
m.put("CC",321);
System.out.println(m);
System.out.println("equals:--->" + m.equals("AA"));
System.out.println("isEmpty:--->" + m.isEmpty());
System.out.println("keySet:--->" + m.keySet());
System.out.println("values:--->" + m.values());
System.out.println("size:--->" + m.size());
System.out.println("containsKey:--->" + m.containsKey("AA"));
System.out.println("containsValue:--->" + m.containsValue(000));
System.out.println("get:--->" + m.get("BB"));
System.out.println("getOrDefault:--->" + m.getOrDefault("BB",""));
System.out.println("keySet:--->" + m.keySet()); //返回所有key构成的set集合 可用Iterator遍历
System.out.println("entrySet:--->" + m.entrySet()); //返回所有key-value构成的set集合
- HashMap里面还有一个实现类:LinkedHashMap集合,该集合为双链表结构,添加了一堆指针,指向前后的元素,且必须保证不出现重复的键。所以LinkedHashMap添加的数据会根据输入的顺序进行排序。对于频繁使用插入修改删除的集合,使用该集合效率会更高。
LinkedHashMap map = new LinkedHashMap();
map.put(1,"abc");
map.put(2,"ac");
map.put(4,"aa");
map.put(3,"ab");
Set set = map.entrySet(); //Map的遍历,将Map转换成Set
Iterator iterator = set.iterator(); //调用Iterator迭代器
while (iterator.hasNext()) {
System.out.println(iterator.next()); //会按照添加数据的顺序输出 1 2 4 3
}
注意:HashMap集合中没有Iterator这个迭代器,如果需要遍历的话,则需要把Map转换成Set集合,再去调用Iterator迭代器进行遍历输出。
- Map接口中还有一个实现类Hashtable,它和HashMap十分相似,区别在于Hashtable是线程安全的,效率低。且HashMap是不能存储null的key值和value值的。但Hashtable类有一个子类Properties在实际应用中非常重要。
- Properties主要用来存储字符串类型的键和值,在实际开发中,经常使用Properties集合来存取应用的配置项。
//常用来处理配置文件,key和value都是String类型
Properties p = new Properties();
FileInputStream f = new FileInputStream("test.properties");
p.load(f); //加载流对应的文件
String name = p.getProperty("name");
String age = p.getProperty("age");
System.out.println(name + "--" + age);
TreeMap集合:
TreeMap能够保证按照添加到集合里面的key-value类型进行排序(必须为同一类型),实现排序遍历。此时,我们就可以考虑key的使用自然排序或定制排序(通过Comparator对比函数进行自定义排序方法),TreeMap的底层其实是使用了红黑树。
Comparator co = new Comparator() { //自定义co排序方法
@Override
public int compare(Object o1, Object o2) {
if (o1 instanceof User && o2 instanceof User) {
User user1 = (User)o1;
User user2 = (User)o2;
return Integer.compare(user1.getAge(),user2.getAge()); 按照age元素进行排序
}
throw new RuntimeException("输入数据不匹配");
}
};
TreeMap map = new TreeMap(co); //TreeMap传入自定义排序方法co
map.put(new User("VX1",1),6);
map.put(new User("VX2",4),5);
map.put(new User("VX5",6),4);
map.put(new User("VX4",3),3);
map.put(new User("VX5",6),2);
map.put(new User("VX5",7),1);
Set set = map.entrySet(); //map转set进行遍历
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
关于Map集合的详解:
Map中的Entry:不可重复,无序的;可以使用Set存储所有entry,可利用Set调用Iterator迭代器进行遍历;
Map中的key:不可重复,无序的;相当于使用Set存储所有key;如果是自定义类,key所在的类就必须要重写equals()和hashCode()方法
Map中的value:可重复的,无序的;相当于使用Colleaction存储所有value;如果是自定义类,value所在的类就必须要重写equals()
图:
HashMap的底层实现原理:(基于jdk8)
HashMap map = new HashMap();
- 在实例化HashMap集合后,底层会自动创建一个没有明确长度的数组Node[] table;
Map.put(key,value)---->
当对map进行第一次put()添加数据时,会生成对数组Entry[] table赋予长度16. - 对于HashMap添加元素。首先,调用key所在类的hashCode()计算key的哈希值,经过算法计算后,得到的Entry数组的某个确定位置
- 如果此位置上数据为空,则添加成功;
- 如果此位置上数据不为空,则意味着有一个或多个数据(以链表形式存在),此时需要比较key值和已经存在的数据的哈希值:
- 如果key的哈希值相同,则元素不重复,添加成功;
- 如果key的哈希值位不相同,则元素重复,添加不成功;继续比较,需要再调用equals()方法进行比较;
- 如果equals()返回false,则添加成功;
- 如果equals()返回true,则添加不成功;使用后添加进去的value的值代替原value的值。(value2代替value1这是跟HashSet最大区别)。
注意:在HashMap的底层中,如果多个数据存储在同个位置,会以链表的方式进行存储。而且当内部长度不够时,会自动扩容,扩容为 原来的2倍,再将原有的数据复制过来。
jdk7与jdk8中HashMap的底层实现原理的区别:(jdk7)
- new HashMap() 时会自动创建一个长度为16的数组;
- jdk8底层的数组是:Node[] , 而jdk7底层的数组是:Entry[] ;
- 再进行put()之前就已经赋予长度为16的数组;
- jdk7的底层:数组+链表;
- jdk8的底层:数组+链表+红黑树 ;当数组的某一个位置上的元素以链表形式存在的数据个数 > 8 ,且当前数组的长度 > 64 ;
此时索引位置上的所有数据都将改为红黑树进行存储(红黑树:左边小,右边大);