1.搜索
1.1 概念及场景
Map和set是一种专门用来进行搜索的容器或者数据结构,其搜索的效率与其具体的实例化子类有关
我们之前学过的搜索方式有:
- 直接遍历,时间复杂度为O(N),元素如果比较多效率会非常慢
- 二分查找,时间复杂度为O(logN) ,但搜索前必须要求序列是有序的
上述排序比较适合静态类型的查找,即一般不会对区间进行插入和删除操作了,而现实中业务的查找比如:
- 根据姓名查询考试成绩
- 通讯录,即根据姓名查询联系方式
- 不重复集合,即需要先搜索关键字是否已经在集合中
可能在查找时进行一些插入和删除的操作,即动态查找,那上述两种方式就不太适合了,因此本章介绍的Map和Set是一种适合动态查找的集合容器
1.2 模型
一般把搜索的数据称为关键字(Key),和关键字对应的称为值(Value),将其称之为Key-value的键值对,所以模型会有两种:
- 纯 key 模型,比如:
① 有一个英文词典,快速查找一个单词是否在词典中
② 快速查找某个名字在不在通讯录中 - Key-Value 模型,比如:
① 统计文件中每个单词出现的次数,统计结果是每个单词都有与其对应的次数:<单词,单词出现的次数>
② 梁山好汉的江湖绰号:每个好汉都有自己的江湖绰号
而之前在刚讲集合框架的时候我们说Map中存储的就是key-value的键值对,现在又告诉大家Set中只存储了Key
2.Map的使用
2.1 关于Map的说明
Map是一个接口类,该类没有继承自Collection,该类中存储的是<K,V>结构的键值对,并且K一定是唯一的,不能重复
2.2 关于Map.Entry<K, V>的说明
Map.Entry<K, V> 是Map内部实现的用来存放<key, value>键值对映射关系的内部类,该内部类中主要提供了<key, value>的获取,value的设置以及Key的比较方式
例如现在给定一个有10万个数据的数组,打印出其中重复的数字和重复的次数
代码示例:
public class TestDemo {
public static void main(String[] args) {
HashMap<Integer,Integer> hashMap = new HashMap<>();
int[] arr = new int[100000];
Random random = new Random();
//生成一个随机数组
for (int i = 0; i < arr.length; i++) {
int num = random.nextInt(100000);
arr[i] = num;
}
//1.遍历数组
//key值放数组元素
//value值放元素出现的次数
for (int val:arr) {
//元素在map中还未重复
if (hashMap.get(val) == null) {
hashMap.put(val,1);
}else {
//存在则让次数加1
int tmp = hashMap.get(val)+1;
hashMap.put(val,tmp);
}
}
//打印每个重复的数字和次数
for (Map.Entry<Integer,Integer> entry:hashMap.entrySet()) {
if (entry.getValue() > 1) {
System.out.println("重复的数字:" + entry.getKey() + " 次数:" + entry.getValue());
}
}
}
}
2.3 Map的常用方法说明
方法 | 解释 |
---|---|
V get(Object key) | 返回 key 对应的 value |
V getOrDefault(Object key, V defaultValue) | 返回 key 对应的 value,key 不存在,返回默认值 |
V put(K key, V value) | 设置 key 对应的 value |
V remove(Object key) | 删除 key 对应的映射关系 |
Set keySet() | 返回所有 key 的不重复集合 |
Collection values() | 返回所有 value 的可重复集合 |
Set<Map.Entry<K, V>> entrySet() | 返回所有的 key-value 映射关系 |
boolean containsKey(Object key) | 判断是否包含 key |
boolean containsValue(Object value) | 判断是否包含 value |
注意:
- Map是一个接口,不能直接实例化对象,如果要实例化对象只能实例化其实现类 TreeMap 或者 HashMap
- Map中存放的键值对的key是唯一的,value是可以重复的
- 在Map中插入键值对时,key值不能为空,否则会报出空指针异常,value可以为空
- Map中的key可以全部分离出来储存到Set中去进行访问,因为key值不能重复
- Map中的value可以全部分离出来储存到Collection的任何一个子集合中,value可以重复
- Map中的key值不能直接修改,value值可以直接修改,如果要修改key的值,必须先将key值删掉,再插入新的key值
- TreeMap 和 HashMap 的区别
Map底层结构 | TreeMap | HashMap |
---|---|---|
底层结构 | 红黑树 | 哈希桶 |
插入/删除/查找时间 | O(logN) | 复杂度 O(1) |
是否有序 | 关于Key有序 | 无序 |
线程安全 | 不安全 | 不安全 |
插入/删除/查找区别 | 需要进行元素比较 | 通过哈希函数计算哈希地址 |
比较与覆写 | key必须能够比较,否则会抛出ClassCastException异常 | 自定义类型需要覆写equals和hashCode方法 |
应用场景 | 需要Key有序场景下 | Key是否有序不关心,需要更高的时间性能 |
2.4 TreeMap的使用案例
下面用TreeMap的实例给大家演示一下上面的方法的使用
代码示例:
import java.util.TreeMap;
import java.util.Map;
public static void TestMap1(){
Map<String, String> m = new TreeMap<>();
// put(key, value):插入key-value的键值对
// 如果key不存在,会将key-value的键值对插入到map中,返回null
m.put("林冲", "豹子头");
m.put("鲁智深", "花和尚");
m.put("武松", "行者");
m.put("宋江", "及时雨");
String str = m.put("李逵", "黑旋风");
System.out.println(m.size());
System.out.println(m);
// put(key,value): 注意key不能为空,但是value可以为空
// key如果为空,会抛出空指针异常
//m.put(null, "花名");
str = m.put("无名", null);
System.out.println(m.size());
// put(key, value):
// 如果key存在,会使用value替换原来key所对应的value,返回旧value
str = m.put("李逵", "铁牛");
// get(key): 返回key所对应的value
// 如果key存在,返回key所对应的value
// 如果key不存在,返回null
System.out.println(m.get("鲁智深"));
System.out.println(m.get("史进"));
//GetOrDefault(): 如果key存在,返回与key所对应的value,如果key不存在,返回一个默认值
System.out.println(m.getOrDefault("李逵", "铁牛"));
System.out.println(m.getOrDefault("史进", "九纹龙"));
System.out.println(m.size