Map
一、Map接口简述
存储双列数据,存储key-value对的数据 ----类似于高中讲的函数
- Map结构的理解:
Map中的key
:无序、不可重复,使用Set存储所有的key —>要求key所在类重写equals()方法和hashCode()方法
Map中的value:无序、可重复使用Collection存储所有的value —>value所在类要重写equals()方法
一个键值对:key-value
构成了一个Entry对象。
Map中的Entry:无序、不可重复,使用Set存储所有的Entry
二、 Map中定义的方法
2.1 添加、删除、修改
object put(Object key,object value)
:将指定 key-value添加到(或修改)到当前map对象中
public void putTest(){
HashMap map = new HashMap();
//添加
map.put("A",'a');
map.put("B",'b');
map.put("C",'c');
map.put("D",'d');
map.put("E",'e');
System.out.println(map);
//修改
map.put("A","aa");
System.out.println(map);
}
输出
{A=a, B=b, C=c, D=d, E=e}
{A=aa, B=b, C=c, D=d, E=e}
void putAll(Map m)
:将m中的所有 key-value对存放到当前map中
public void test(){
HashMap map = new HashMap();
//添加
map.put("A",'a');
map.put("B",'b');
map.put("C",'c');
map.put("D",'d');
map.put("E",'e');
System.out.println(map);
HashMap map1 = new HashMap();
map1.putAll(map);
System.out.println(map1);
}
输出:
{A=a, B=b, C=c, D=d, E=e}
{A=a, B=b, C=c, D=d, E=e}
Object remove(Object key)
:移除指定key的key-value对,并返回value
System.out.println(map.remove("A"));
System.out.println(map.remove("a"));
System.out.println(map);
输出:
aa
null
{B=b, C=c, D=d, E=e}
void clear
:清空当前map中的所有数据
map.clear();
System.out.println(map);
System.out.println(map.size());
输出:
{}
0
2.2 元素查询的操作:
Object get(Object key)
:获取指定key对应的value
System.out.println(map.get("D"));
System.out.println(map.get("DD"));
d
null
boolean containsKey(Object key)
:是否包含指定的key
System.out.println(map.containsKey("E"));
System.out.println(map.containsKey("EE"));
true
false
boolean containsValue(Object value)
:是否包含指定的value
System.out.println(map.containsValue("a"));
System.out.println(map.containsValue('a'));
System.out.println(map.containsValue("asbha"));
false
true
false
int size()
;返回map中的 key-value 的个数
System.out.println(map.size());
5
boolean isEmpty()
:判断当前map是否为空
System.out.println(map.isEmpty());
map.clear();
System.out.println(map.isEmpty());
false
true
boolean equals(Object obj)
:判断当前map和参数对象obj是否相等
HashMap map1 = new HashMap();
//添加
map1.put("A",'a');
map1.put("B",'b');
map1.put("C",'c');
map1.put("D",'d');
map1.put("E",'e');
System.out.println(map.equals(map1));
HashMap map2 = new HashMap();
//添加
map2.put("A",'a');
map2.put("B",'c');
map2.put("C",'c');
map2.put("D",'d');
map2.put("E",'e');
System.out.println(map.equals(map2));
true
false
2.3 元视图操作的方法:
Set keySet()
:返回所有key构成的Collection集合
Collection values()
:返回所有value构成的Collection集合
Set entrySet()
:返回所有key-value对构成的Set集合
HashMap map = new HashMap();
//添加
map.put("A",'a');
map.put("B",'b');
map.put("C",'c');
map.put("D",'d');
map.put("E",'e');
//遍历
// `` Set keySet()``:返回所有key构成的Collection集合
Set set = map.keySet();
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("####");
// ``Collection values()``:返回所有value构成的Collection集合
Collection values = map.values();
for (Object obj : values){
System.out.println(obj);
}
System.out.println("####");
// ``Set entrySet()``:返回所有key-value对构成的Set集合
Set entrySet = map.entrySet();
Iterator iterator1 = entrySet.iterator();
while (iterator1.hasNext()){
Object obj = iterator1.next();
//entrySet集合中的元素都是entry
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey() + "--->" + entry.getValue());
}
A
B
C
D
E
####
a
b
c
d
e
####
A--->a
B--->b
C--->c
D--->d
E--->e
三、 Map实现类
3.1 HashMap
作为Map的主要实现类
HashMap是线程不安全的,但是效率高
可以存储null的key或者value
HashMap底层:数组+链表(jdk7及以前),数组+链表+红黑树(jdk8)
3.1.1 LinkedHashMap
保证在遍历Map元素时,可以按照添加的顺序实现遍历
原因:在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素
对于频繁的遍历操作,此类执行效率高于HashMap
3.2 TreeMap
保证按照添加的key-value对进行排序,实现遍历排序。此时考虑key的自然排序,或者定制排序
底层使用红黑树
3.2.1 Tree的两种添加方式
- 向TreeMap中添加key-value,要求key必须是由同一个类创建的对象,一位key是排序的依据
public void test1(){
TreeMap map = new TreeMap();
map.put("AA", 'a');
map.put(1,'c');
}
报错信息:
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
自然排序
@Test
public void test1(){
TreeMap map = new TreeMap();
User u1 = new User("Tom", 19);
User u2 = new User("Alice", 51);
User u3 = new User("Bob", 19);
User u4 = new User("Jerry", 23);
User u5 = new User("Jimi", 25);
map.put(u1,98);
map.put(u2,45);
map.put(u3,65);
map.put(u4,18);
map.put(u5,54);
Set entrySet = map.entrySet();
Iterator iterator1 = entrySet.iterator();
while (iterator1.hasNext()){
Object obj = iterator1.next();
//entrySet集合中的元素都是entry
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey() + "--->" + entry.getValue());
}
}
输出:
User{name='Tom', age=19}--->98
User{name='Jimi', age=25}--->54
User{name='Jerry', age=23}--->18
User{name='Bob', age=19}--->65
User{name='Alice', age=51}--->45
定制排序
@Test
public void test2(){
TreeMap map = new TreeMap(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
if(o1 instanceof User && o2 instanceof User){
User u1 = (User) o1;
User u2 = (User) o2;
return Integer.compare(u1.getAge(),u2.getAge());
}
throw new RuntimeException("输入的类型不匹配");
}
});
User u1 = new User("Tom", 19);
User u2 = new User("Alice", 51);
User u3 = new User("Bob", 19);
User u4 = new User("Jerry", 23);
User u5 = new User("Jimi", 25);
map.put(u1,98);
map.put(u2,45);
map.put(u3,65);
map.put(u4,18);
map.put(u5,54);
Set entrySet = map.entrySet();
Iterator iterator1 = entrySet.iterator();
while (iterator1.hasNext()){
Object obj = iterator1.next();
//entrySet集合中的元素都是entry
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey() + "--->" + entry.getValue());
}
}
输出:
User{name='Tom', age=19}--->65
User{name='Jerry', age=23}--->18
User{name='Jimi', age=25}--->54
User{name='Alice', age=51}--->45
3.3 Hashtable
作为古老的实现类
线程安全,但是效率低
不能存储null的key或者value
3.3.1 Properties
常用来处理配置文件。key和value都是String类型
应用场景
配置文件jdbc.properties
name=Tom
password=abc123
java文件
public static void main(String[] args) {
FileInputStream fis = null;
try {
Properties pros = new Properties();
fis = new FileInputStream("jdbc.properties");
pros.load(fis);//加载对应的文件
String name = pros.getProperty("name");
String password = pros.getProperty("password");
System.out.println("name = " + name + ",password = " + password);
}catch (IOException e){
e.printStackTrace();
}finally {
if(fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
输出:
name = Tom,password = abc123
四、Map相关面试题
4.1 HashMap底层实现原理?
jdk7
HashMap map = new HashMap();
//在实例化后,底层创建了长度是16的一维数组Entry[] table
map.put(key1,value1);
//首先计算key1所在类的hashCode方法计算key1哈希值,此哈希值结果某种计算后得到在Entry数组中的存放位置
//如果此位置上的数据为空,此时的key1-value1 添加成功
//如果此位置上的数据不为空,(意味着,此位置上,存在一个或多个数据(以链表的形式存在)),比较key1和已经存在的数据的哈希值:
//如果key1的哈希值与已经存在的数据的哈希值都不同,此时key1-value1就添加成功
//如何key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)方法比较:
//如何返回false:此时key1-value1添加成功
//如果返回true:使用value1替换value2
//
//
不断地添加过程中,会涉及到扩容问题,默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来
jdk8
- jdk8底层的数组是:
Node[]
,而非Entry[] - 首次调用
put
方法时,底层创建长度为16的数组 - jdk7底层结构只有:
数组+链表
。jdk8中底层结构:数组+链表+红黑树
当数组的某一个多余位置上的元素以链表形式存储的数据个数 > 8 ,且当前数组的长度 > 64时,此时此索引位置上的所有数据改为红黑树存储(jdk8中的核心修改)
new HashMap()//底层没有创建一个长度为16的数组