一、Map概述
双列集合是每个元素都有键与值两部分组成的集合,记录的是键值对映射关系,将键值对存储到容器中,在查找的时候可以通过键找到值。
Map集合体系图
双列集合:Map<K,V>
如果实现了Map接口的集合类,具备特点:存储的数据都是以键值对的形式存在的,键不可重复,值可以重复
-----| HashMap
-----| TreeMap
-----| HashTable
Map集合特点:
1、Map集合是一个双列集合,一个元素包含两值(一个key,一个value)
2、Map集合中的元素,key和value的数据类型可以相同,也可以不同
3、Map集合中的元素,key是不允许重复的,value是可以重复的
4、Map集合中的元素,key和value是一一对应的
Map常用API
V put(K key, V value);
//存储键值,如果键值存在,则使用新值替换旧值,返回旧值。如果键不存在,则直接存储,返回null
V remove(Object key);
//根据键删除键值对,返回键对应的值。如果键不存在,则返回null
V get (Object key);
//根据键找对应的值,如果键存在,则返回null。
Int size();
//获得键值对的个数
boolean containsKey(Object key);
//判断集合中是否包含对应的键,包含返回true,否则返回false
boolean isEmpty();
//判断集合是否为空,有元素返回false,否则返回true
遍历Map集合的四种方式
1、keySet(); keySet方法是将Map中的所有键存到一个Set集合中,并返回Set集合。
缺点:返回键,还要去找值,遍历效率低。
2、values(); 将所有的value存放到一个collection集合中返回。
缺点:只能返回所有的值,没有键
3、entrySet();//entrySet 将所有的Key和Value存放到一个Entry实现中返回
4、过forEach();方法遍历(JDK1.8特性)
方法一:keySet();实现遍历
结果:
键:语文,值:112
键:英语,值:80
键:体育,值:100
键:数学,值:91
其中Map集合中的长度是4
方法二:values(); 遍历
结果:
值:112
值:80
值:100
值:91
方法三:entrySet(); 遍历
结果:
键:语文
值:112
键:英语
值:80
键:体育
值:100
键:数学
值:91
方法四:通过forEach方法进行迭代
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("语文",110);
map.put("数学",93);
map.put("英语",26);
map.forEach((key, value) -> {
System.out.println(key + ":" + value);
});
}
Map接口有三个比较重要的实现类,分别是HashMap、TreeMap和HashTable。
1.1、HashMap
java.util.HashMap<K,V>集合 implements Map<K,V>接口
HashMap集合的特点:
1.HashMap集合底层是哈希值:查询的速度特别的快
JDK.8之前:数组+单向链表
JDK.8之后:数组+单向链表/红黑树(链表的长度超过8):提高查询的速度
2.HashMap集合是一个无序的集合,存储元素和取出元素的顺序有可能不一致
HashMap:底层是一个哈希表,是一个线程不安全的集合,是多线程的集合,速度快
HashMap集合:可以存储null值,null键
HashMap的存储原理:
往HashMap添加元素的时候,首先会调用键的hashCode方法得到元素的哈希值,然后经过运算算出该元素在哈希表中的存储位置。
情况一:
如果算出的位置目前没有任何元素存储,那么该元素可以直接添加到哈希表中。
情况二:
如果算出的位置目前已经存在其他元素,那么还会调用该元素的equals方法与这个位置上的元素进行比较,如果equals方法返回的是false,那么该元素允许被存储,如果equals方法返回的是true,那么该元素被视为重复元素,不允许存储。
HashMap和Set集合区别:
1、HashMap和Set底层都是基哈希表实现的,判断元素是否重复都需要重写equals和HashCode方法
2、HashMap是双列集合,Set是单列集合,都是存储无序的集合
3、HashMap如果存在重复元素,后面存的元素会覆盖前面的Set集合如果存在重复元素,那么元素将不被存入。
1.2、LinkedHashMap
HashMap保证了元素唯一,并且查询速度很快,可是成对元素存入进去的时候是没有顺序的,那么要保证存储有序,还需要速度怎么办呢?
在HashMap下有一个子类LinkedHashMap,它是链表和哈希表组合的一个数据存储结构,记录元素的顺序,所以LinkedHashMap是一个有序的集合。
例如:
LinkedeHashMap存储顺序一致,不可重复。
HashMap是存储顺序不一致,也是不可重复。
更推荐使用LinkedeHashMap
1.3、HashTable
java.util.Hashtable<K,V>集合 implements Map<k,V>接口
HashTable特点:
Hashtable:底层也是一个哈希表,是一个线程安全的、同步的集合,是单线程集合,速度慢
Hashtable集合:不能存储null值,null键
Hashtable集合和Vector集合一样,在jdk1.2版本之后被更先进的集合(HashMap,ArrayList)取代了
Hashtable的子类properties依然活跃在历舞台
Properties集合是一个唯一和I/O流相结合的集合
面试题:
Hashtable 和 HashMap的区别:
(1) Hashtable 线程同步,安全,速度慢
(2) HashMap 线程不同步,不安全,速度快
(3) 前者不允许使用null键null值 , 后者允许使用null键null值
1.4、TreeMap
java.util.TreeMap<K,V>集合 implements Map<k,V>接口
TreeMap 主要事项:
1、往TreeMap添加元素的时候,如果元素的键具备自然顺序,那么就会按照键的自然顺序进行排列存储
2、如果键不具备自然顺序特性,那么键所属于的类必须要实现Comparable接口,把键的比较规则方法卸载comparaTo方法上
3、如果元素的键不具备自然排特性,而且键所属的类也没有实现Comparable接口,那么就必须在创建TreeMap对象的时候传如比较器
TreeMap使用案例:
Exam类实现比较器
class Exam implements Comparable<Exam>{
int score; //成绩
String subject;
public Exam(int score, String subject) {
super();
this.score = score;
this.subject = subject;
}
@Override
public String toString() {
return this.subject+":"+this.score;
}
@Override
public int compareTo(Exam o) {
return this.score - o.score;
}
}
自定义比较器
class My_comparator implements Comparator<Exam>{
@Override
public int compare(Exam o1, Exam o2) {
return o1.score - o2.score;
}
}
main方法:
public static void main(String[] args) {
//1、具有排序特性的对象
TreeMap<Character,Integer> tree = new TreeMap<Character,Integer>();
tree.put('b', 10);
tree.put('a', 66);
tree.put('d', 28);
tree.put('c', 102);
System.out.println(tree); //具有排序特性的键会按键的排序特性排序输出
//2、不具备排序特性的对象
TreeMap<Exam,String> tree2 = new TreeMap<Exam,String>();
tree2.put(new Exam(91,"数学"), "001");
tree2.put(new Exam(86,"英语"), "002");
tree2.put(new Exam(110,"语文"), "003");
tree2.put(new Exam(60,"物理"), "004");
System.out.println("tree2:"+tree2); //不具有排序特性的键需要重写
//3、不具备排序特性的对象,且不实现comparable接口,必须传入比较器
My_comparator comparator = new My_comparator();
TreeMap<Exam,String> tree3 = new TreeMap<Exam,String>(comparator);
tree3.put(new Exam(91,"数学"), "001");
tree3.put(new Exam(86,"英语"), "002");
tree3.put(new Exam(110,"语文"), "003");
tree3.put(new Exam(60,"物理"), "004");
System.out.println("tree3:"+tree3); //不具有排序特性的键需要重写
}
结果:
{a=66, b=10, c=102, d=28}
tree2:{物理:60=004, 英语:86=002, 数学:91=001, 语文:110=003}
tree3:{物理:60=004, 英语:86=002, 数学:91=001, 语文:110=003}
HashTable 是线程安全的一个集合,不允许 null 值作为一个 key 值或者 Value 值;
HashTable 是 sychronize,多个线程访问时不需要自己为它的方法实现同步,而 HashMap 在被多个线程访问的时候需要自己为它的方法实现同步
总结:
(1)TreeMap是有序存储的,HashMap和HashTable是无序存储的。
(2)HashMap 是线程不安全的,HashMap 是一个接口,是 Map 的一个子接口,是将键映射到值得对象,不允许键值重复,允许空键和空值;由于非线程安全。HashMap 的效率要较 HashTable 的效率高一些.
(3)HashTable 是线程安全的一个集合,不允许 null 值作为一个 key 值或者 Value 值;
(4)HashTable 是 sychronize,多个线程访问时不需要自己为它的方法实现同步,而 HashMap 在被多个线
程访问的时候需要自己为它的方法实现同步。
二、JDK9对集合添加的优化
在JDK9中,添加了集中集合工厂方法,更方便创建少量元素的集合和map实例。新的List、Set、Map的静态工厂方法可以更方便的创建集合的不可变实例。
JDK9新特性
List接口、Set接口、Map接口:里面增加了一个静态的方法of,可以给集合一次性添加多个元素
static <E> List<E> of (E...elements)
使用前提:
当集合中存储的元素个数已经确定了,变数不在改变时候使用。注意事项:
1、of方法只使用于List接口、Set接口、Map接口。不使用于接口的实现类
2、of方法返回值是一个不能改变的集合,单列集合不能再使用add,双列集合不能使用put方法添加元素,会抛出异常。
3、除List集合外,Set接口和Map接口在调用of方法的时候不能有重复的元素,否则会抛出异常。
例如:
public static void main(String[] args) {
//List可存储重复元素
List<String> list = List.of("a","b","c","d","a","c");
System.out.println(list.toString());
//list.add("w"); false,使用了of之后元素个数确定,不能再使用add方法增加元素
}
public static void main(String[] args) {
//Set不可存储重复元素
Set<String> list = Set.of("a","b","c");
System.out.println(list.toString());
}
//Map不可存储重复元素
Map<String,Integer> map = Map.of("语文",110,"数学",92,"英语",92);
System.out.println(map.toString());
//map.put("化学",88); false