1、概述
Map 是 Java 集合体系中的另一个体系,之前的是 Collection。当然并不是说这个两个体系之间没有了交集,这样只是说学习起来比较让人容易接受。
Map 体系的最顶层是
java.util.Map
这个接口,这与前面的java.util.Collection
接口是一样的思路。
Map 与前面的 Collection 不同,存入其中的元素是成对的,这个每一对元素用一个专业的术语就是“键值对”,每一对元素,其中一个是“键”(key),另一个是“值”(value),“键”与“值”是一一对应的。Map 就是用来存放多个“键值对”的这样一个集合。形象的说法是,将一个 Map 比喻成一本书,所有的“键”构成了这本书的目录,目录会告诉读者对应的内容在什么地方,也就是通过“键”就能找到“值”。
在 Map 中,最基本的规则是,“键”不允许重复、“键”与“值”必须保证是一对一的关系。
java.util.Map
同样是支持使用泛型的,而通过上面的解读,可以大概清楚其泛型是怎么指定的,Map<K, V>
,键与值的泛型都要分别指定。java.util.Map
的主要的实现类有:java.util.HashMap
、java.util.LinkedHashMap
、java.util.TreeMap
、java.util.Hashtable
、java.util.Properties
等。java.util.HashMap
底层所采用的数据结构是哈希表。之前的java.util.HashSet
从本质上说是一个 HashMap 。
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable {
······
private transient HashMap<E,Object> map;
public HashSet() {
map = new HashMap<>();
}
······
}
-
java.util.TreeMap
底层所采用的数据结构是红黑树。之前的java.util.TreeSet
从本质上来说是一个 TreeMap 。和java.util.TreeSet
一样,放入其中的元素也是必须具有可比性的。
public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, java.io.Serializable {
······
public TreeSet() {
this(new TreeMap<>());
}
······
}
java.util.LinkedHashMap
类与java.util.HashMap
类的不同之处在于内部维护了一个双向链表,通过链表中记录了元素的迭代顺序,也就是元素插入集合中的先后顺序,因此便于迭代。java.util.Hashtable
类是一个老的java.util.Map
实现类,与java.util.HashMap
类相比属于线程安全的类,且不允许null
作为 key 或者 value 的数值。java.util.Properties
类是java.util.Hashtable
类的子类,该对象用于处理属性文件,属性文件中的 key 和 value 都是java.util.String
类型的。注意,java.util.Properties
类是不需要指定泛型的!
Properties properties = new Properties();
- Map 与 Set 之间有比较密切的关系,Set 的底层是在通过 Map 实现的。Set 中的元素实际上是作为 Map 中的 key 。
2、Map 接口中的常用方法
- 添加与修改
import java.util.HashMap;
import java.util.Map;
public class MapTest {
public static void main(String[] args) {
Map<Integer, String> map = new HashMap<>();
// 对于 Map 中不存在的“键”,使用 put 方法可以添加
map.put(1, "张三");
map.put(2, "李四");
System.out.println(map); // {1=张三, 2=李四}
// 对于 Map 中存在的“键”,使用 put 方法是修改
map.put(1, "王五");
System.out.println(map); // {1=王五, 2=李四}
}
}
- 查找
import java.util.HashMap;
import java.util.Map;
public class MapTest {
public static void main(String[] args) {
Map<Integer, String> map = new HashMap<>();
map.put(1, "张三");
map.put(2, "李四");
map.put(3, "王五");
boolean b1 = map.containsKey(1);
System.out.println(b1); // true
boolean b2 = map.containsValue("张思");
System.out.println(b2); // false
Object obj1 = map.get(5);
System.out.println(obj1); // null
Object obj2 = map.get(3);
System.out.println(obj2); // 王五
}
}
- 删除
import java.util.HashMap;
import java.util.Map;
public class MapTest {
public static void main(String[] args) {
Map<Integer, String> map = new HashMap<>();
map.put(1, "张三");
map.put(2, "李四");
map.put(3, "王五");
System.out.println(map); // {1=张三, 2=李四, 3=王五}
String s1 = map.remove(5);
System.out.println(s1); // null
String s2 = map.remove(1);
System.out.println(s2); // 张三
System.out.println(map); // {2=李四, 3=王五}
}
}
- Map 的遍历
java.util.Map
接口并没有继承java.util.Iterator
这个接口,所以,不能使用 for-each 来遍历 Map!
1、使用keySet
方法,获取包含有全部“键”的 Set,然后通过get
方法进而实现对 Map 的遍历。
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapTest {
public static void main(String[] args) {
Map<Integer, String> map = new HashMap<>();
map.put(1, "张三");
map.put(2, "李四");
map.put(3, "王五");
System.out.println(map);
Set<Integer> set = map.keySet();
for (Integer key : set) {
System.out.println(key + "=" + map.get(key));
}
}
}
{1=张三, 2=李四, 3=王五}
1=张三
2=李四
3=王五
2、使用values
方法,获取 Map 中全部的“值”。但是,这种方法对于想获取“值”对应的“键”而言并不友好。
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class MapTest {
public static void main(String[] args) {
Map<Integer, String> map = new HashMap<>();
map.put(1, "张三");
map.put(2, "李四");
map.put(3, "王五");
System.out.println(map);
Collection<String> collection = map.values();
for (String value : collection) {
System.out.println(value);
}
}
}
{1=张三, 2=李四, 3=王五}
张三
李四
王五
3、使用entrySet
方法,这个方法可以将 Map 中所有的键值对就是以成对的形式放入 Set 中。这个方法的返回值是Set<Map.Entry<K, V>>
,这个Map.Entry<K, V>
是java.util.Map
中的一个内部接口。
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapTest {
public static void main(String[] args) {
Map<Integer, String> map = new HashMap<>();
map.put(1, "张三");
map.put(2, "李四");
map.put(3, "王五");
System.out.println(map);
Set<Map.Entry<Integer, String>> set = map.entrySet();
for (Map.Entry<Integer, String> entry : set) {
System.out.println(entry.getKey() + "=" + entry.getValue());
}
}
}
{1=张三, 2=李四, 3=王五}
1=张三
2=李四
3=王五
3、HashMap 放入元素的大概过程
- 之前提到过
java.util.HashSet
的本质上是java.util.HashMap
,所以,对于向java.util.HashMap
中放入元素的过程是和java.util.HashSet
是相似的,只不过,在这个过程中一系列的计算是基于元素中那个作为“键”的对象。
- 对于哈希表这个数据结构而言,一般情况是“数组+链表”这样的一种形式,但是在 Java 的 HashMap 中,为了提高查询效率,在一定条件下,“数组+链表”的形式会变为“数组+红黑树”的形式。