Java集合/Map个人笔记
———————————————————————————————————————————————————————
Java中的集合——Map
- Java 中主要有两种集合:Collection 接口和 Map 接口。
———————————————————————————————————————————————————————
1. Map 的特点
- 保存 Key-Value对 形式的元素,访问时只根据每项元素的 key 来访问其 value。
- 每个 key 都是唯一的,不可重复;但 value 是可以重复的。
- 有些映射可以接收 null key 和 null value ,而有的则不行。
- Map 不能直接通过 foreach 或迭代器进行遍历,需要把它先转成 Set 集合。
2. Map 下的实现类
- Map 接口下有两个实现类:
- HashMap
- HashTable
- TreeSet
*当元素的顺序很重要时选用 TreeMap;当元素不必以特定的顺序进行存储时,使用 HashMap。
*HashMap 不是同步的,HashTable 是同步的;但 HashTable 不推荐使用,因为 HashMap 提供了所有类似的功能,并且速度更快。
*当需要在多线程环境下使用时,HashMap 也可以转换为同步的。
HashMap 可以通过 Map m = Collections.synchronizedMap(hashMap) 来达到同步的效果。
3. Map 中的方法
- put(K key, V value)
将指定的值与该映射中的指定键相关联(可选操作)。 - get(Object key)
返回到指定键所映射的值,或 null如果此映射包含该键的映射。 - clear()
从该 Map 中删除所有的映射(可选操作)。 - containsValue(Object value)
如果此 Map 将一个或多个键映射到指定的值,则返回 true。 - entryset()
返回此 Map 中包含的映射的 Set 视图。 - keySet()
返回 Map 中 key 的集合。 - equals(Object o)
将指定的对象与此映射进行比较以获得相等性。
import java.util.HashMap;
import java.util.Map;
public class MapDemo {
public static void main(String[] args) {
Map objectHashMap = new HashMap();
// Map(key,value)
// 添加集合键值对,如果不指定泛型,键和值都为 Object 类型
// key 不允许重复
objectHashMap.put("张三",35); // 实现了自动装箱
objectHashMap.put("李四",30); // 实现了自动装箱
objectHashMap.put("王五",33); // 实现了自动装箱
objectHashMap.put("赵六",31); // 实现了自动装箱
objectHashMap.put("张三",32);
// 覆盖原有的 为 "张三" 的 key
objectHashMap.put(null,null);
// 允许存在空键,但只能有一个;可以有多个空值
/*
* HashMap 的方法与 HashTable 基本一致
*/
// size()
// 获取集合的长度
int size = objectHashMap.size();
System.out.println("size = " + size);
// size = 5,因为 key 是不允许重复的,每个 key 都是唯一的
// get(Object key)
// 返回指定键所映射的值,如果此映射不包含该键的映射关系,则返回 null
Object w5 = objectHashMap.get("王五");
System.out.println("w5 = " + w5);
// w5 = 33,根据 key 获取 value
Object z3 = objectHashMap.get("张三");
System.out.println("z3 = " + z3);
// z3 = 32
Object obj = objectHashMap.get(null);
System.out.println("obj = " + obj);
// obj = null
// clear()
// 清除集合中的所有元素
/*objectHashMap.clear();*/
// containsKey(Object key)
boolean a = objectHashMap.containsKey("张三");
System.out.println("a = " + a); // true
// containsValue(Object value)
boolean b = objectHashMap.containsValue(36);
System.out.println("b = " + b); // false
// isEmpty()
// 如果此 Map 不包含 键值映射关系,则返回 true
boolean empty = objectHashMap.isEmpty();
System.out.println("empty = " + empty); // false
// remove(Object key)
// 如果存在该键的 键值映射关系,则将其从该 Map 中删除
objectHashMap.remove("赵六");
System.out.println("size = " + objectHashMap.size()); // 4
}
}
———————————————————————————————————————————————————————
HashMap
1. HashMap 的特点
- HashMap 是线程不安全的,key 可以为 null。
- 当元素不必以特定的顺序进行存储时,使用 HashMap。
- 内部定义了一个 Hash 表。以 Hash 表数据结构实现。
- 元素会通过 Hash 转换函数 将元素的 Hash 地址转化成数组中存放的索引。
- HashMap 不是同步的,HashTable 是同步的,但 HashTable 不推荐使用,因为 HashMap 提供了所有类似的功能,并且速度更快。
- 当需要在多线程环境下使用时,HashMap 也可以转换为同步的。
HashMap 可以通过 Map m = Collections.synchronizedMap(hashMap) 来达到同步的效果。
2. HashMap 底层实现
-
内部定义了一个 Hash 表数组。
*Hash表是数组和链表的结合。
-
以 Hash 表数据结构实现(初始长度为 16),查找对象时通过 Hash 函数计算其位置,为快速查询而设计。
-
JDK1.8 之前,由数组 + 链表组成。数组是 HashMap 主体,链表主要为了解决哈希冲突而存在的。
如果定位到数组位置不含链表,对于查找,添加等操作很快,仅需一次寻址。
如果定位到数组位置含链表,对于添加操作,时间复杂度为 O(1),最新的 Entry(集合当中的 key 对象)会插入链表头部,只需简单改变引用链表即可。
查找需要遍历链表,通过 key 对象的 equals 方法逐一比对查找。*拉链法:
将链表和数组结合,创建一个链表数组,数组中每一格就是一个链表,若遇到Hah冲突,则将冲突的值加到链表中即可。 -
JDK1.8 版本之后:
为了解决 Hash 冲突时有了较大的变化,当链表长度大于阈值(默认为 8),链表转换为红黑树,以减小搜索时间。
TreeMap,TreeSet 以及 JDK1.8 之后的 HashMap 底层都用了红黑树。红黑树为了解决二叉树的缺陷,二叉树在某些情况下会退化成一个线性结构。 -
元素会通过 Hash 转换函数 将元素的 Hash 地址转化成数组中存放的索引。
3. 通过 entrySet()、keySet() 两种方法遍历 HashMap
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class EntrySetDemo {
public static void main(String[] args) {
HashMap<String,Integer> map = new HashMap<>();
map.put("张三",35);
map.put("李四",30);
map.put("王五",33);
map.put("赵六",31);
// 把 Map 集合转换成 Set 集合,类型是 Map 接口里的内部接口
Set<Map.Entry<String,Integer>> entries = map.entrySet();
// 遍历 set 集合,通过 entry.getKey()、entry.getValue()方法分别获取键和值
for (Map.Entry<String,Integer> entry:entries
) {
System.out.println(entry.getKey() + "=" + entry.getValue());
}
// 把 Map 集合中所有的 key 转换成一个 Set 集合,
// 再根据 Map 集合本身的 get(Object key) 方法,就可以获取到 value 值了
Set<String> stringSet = map.keySet();
for (String string : stringSet) {
Integer integer = map.get(string);
System.out.println(string + "=" + integer);
}
// 其他组合
/*Set<Integer> integers = map.keySet();
Iterator iter = integers.iterator();
while (iter.hasNext()) {
Object key = iter.next();
User user = map.get(key);
int a = user.getAge();
String n = user.getName();
System.out.println(n + " 的年龄是:" + a);
}*/
}
}
Extra. 两种方法遍历再解
/**
* Map.Entry<String, Integer>
* Map.Entry 内部类
* <String, Integer> 自定义泛型
*/
// 第一种遍历方式
System.out.println("第一种遍历方式:");
// entrySet()
// 把 map 转换为 set 集合
Set<Map.Entry<String