双列集合Map相关面试题
一、了解Map集合吗?Map集合都有哪些实现
- HashMap
- HashTable
- LinkedHashMap
- TreeMap
- ConcurrentHashMap
二、HashMap和HashTable之间的区别
1、定义
HashMap底层基于数组+单向链表(红黑树),非线程安全,允许有空的键和值
-
- 数组:Node<K,V> [] table ,每一个元素都是一个Node
- 单向链表:Node<K,V> next,当发生Hash碰撞,会追加链表,当链表长度大于8,那就转换为红黑树
HashTable底层基于哈希表实现,线程是安全的,不允许有空的键和值
2、继承
HashMap继承自AbstractMap类
HashTable是继承自Dictionary类
3、扩容原理
HashMap的初始容量为16,之后每次扩容,容量就变成了原来的2倍;
HashTable的初始容量为11,之后每次扩容,容量就变成了原来的2n+1;
三、hashCode()和equals()方法使用场景
hashCode():
顶级父类Object当中的方法,返回值类型为int类型的值,根据一定的规则(存储地址,字段,长度等等)生成一个数组,数据保存的就是Hash值
equals():
顶级类Object中的方法,根据一定的比较规则,判断对象是否一致,底层一般逻辑:
1.判断两个对象的内存地址是否一样
2.非空判断和Class类型判断
3.强转
4.对象中的字段一一匹配
package com.zn.mytest; import java.util.Objects; public class UserTest { private int uid; private String uName; public int getUid() { return uid; } public void setUid(int uid) { this.uid = uid; } public String getuName() { return uName; } public void setuName(String uName) { this.uName = uName; } @Override public boolean equals(Object o) { //判断两个对象内存地址是否一样 if (this == o) return true; //非空判断和class判断 if (o == null || getClass() != o.getClass()) return false; //强转 UserTest userTest = (UserTest) o; //对象中的字段--匹配 return uid == userTest.uid && Objects.equals(uName, userTest.uName); } @Override public int hashCode() { return Objects.hash(uid, uName); } }
四、HashMap和TreeMap应该如何选择
HashMap:
底层采用数组+链表(红黑树)结构,可以实现快速的存储和检索,但是数据是无序的,适用于在Map当中插入删除或者获取元素
TreeMap:
存储结构是一个平衡二叉树,具体实现方式为红黑树,默认采用自然排序,可以自定义排序规则,但是需要实现Comparator接口能够便捷的实现内部元素的各种排序,但是性能比HashMap差,适用于按照自然排序和自定义排序规则
五、Set和Map的关系
Set核心就是保存不重复的元素,存储一组唯一的对象
set当中每一种实现都对应Map
HashSet对应的HashMap,TreeSet对应的TreeMap
HashSet对应的底层就是new HashMap,TreeSet对应的底层就是new TreeMap;
HashSet:
TreeSet:
六、常见的Map排序规则
按照添加规则使用LinkedHashMap,按照自然排序或者自定义规则排序可以采用TreeMap
1、TreeMap排序
TreeMap只能根据key进行排序,TreeMap本身是个二叉树,元素的顺序是由key的值决定的;
按照自然排序可以采用TreeMap;
实现代码:
package com.zn.mytest; import java.util.TreeMap; public class TreeMapTest { public static void main(String[] args) { TreeMap<String, String> map = new TreeMap<String,String>(); map.put("1","A"); map.put("2","B"); map.put("3","C"); map.put("4","D"); map.put("5","E"); System.out.println(map); } }
效果展示:
2、HashMap排序
HashMap本身是没有顺序的,不能直接对其进行排序;
要排序,只能先转成list;
实现代码:
package com.zn.mytest; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; public class HashMapTest { public static void main(String[] args) { HashMap<Integer,String> map = new HashMap<Integer,String>(); map.put(1,"A"); map.put(3,"B"); map.put(2,"C"); map.put(4,"D"); map.put(5,"E"); ArrayList<Object> list = new ArrayList<>(); for (Map.Entry<Integer,String> entry:map.entrySet()){ System.out.println("entry.getKey.hash:"+entry.getKey().hashCode()); list.add(entry.getValue()); } System.out.println(list); } }
效果展示:
3、LinkedHashMap排序
插入顺序,遍历LinkedHashMap时,先得到的记录肯定是先插入的;
实现代码:
package com.zn.mytest; import java.util.LinkedHashMap; public class LinkedListTest { public static void main(String[] args) { LinkedHashMap<String, String> map = new LinkedHashMap<>(); for (int i=0;i<10;i++){ map.put("key"+i,"value"+i); } map.get("key"+3); for (String value:map.keySet()){ System.out.println(value); } } }
效果展示:
七、如何保证Map线程安全
多线程环境下,可以使用concurrent包下有一个ConcurrentHashMap或者是使用Collections.synchronizedList(new HashMap<K,V>());
ConcurrentHashMap保证线程安全,效率比HashTable高,采分段锁