文章目录
Map和Set
这里 好像 目录有点问题set 下面的方法没有显示。
搜索
1.1概念及场景
Map和set是一种专门用来进行搜索的容器或者数据结构,其搜索的效率与其具体的实例化子类有关。以前常见的
搜索方式有:
- 直接遍历,时间复杂度为O(N),元素如果比较多效率会非常慢
- 二分查找,时间复杂度为 ,但搜索前必须要求序列是有序的
上述排序比较适合静态类型的查找,即一般不会对区间进行插入和删除操作了,而现实中的查找比如: - 根据姓名查询考试成绩
- 通讯录,即根据姓名查询联系方式
- 不重复集合,即需要先搜索关键字是否已经在集合中
可能在查找时进行一些插入和删除的操作,即动态查找,那上述两种方式就不太适合了,本节介绍的Map和Set是
一种适合动态查找的集合容器
1.2模型
一般把搜索的数据称为关键字(Key),和关键字对应的称为值(Value),将其称之为Key-value的键值对,所以
模型会有两种:
-
纯 key 模型,比如:
有一个英文词典,快速查找一个单词是否在词典中
快速查找某个名字在不在通讯录中 -
Key-Value 模型,比如:
统计文件中每个单词出现的次数,统计结果是每个单词都有与其对应的次数:<单词,单词出现的次数>
梁山好汉的江湖绰号:每个好汉都有自己的江湖绰号
而Map中存储的就是key-value的键值对,Set中只存储了Key
map使用
注意:Map是一个接口类,该类没有继承自Collection,该类中存储的是<K,V>结构的键值对,并且K一定是唯一的,不
能重复
这里 可以 看到 map 实现的 接口 hashMap 和 TreeMap,这里 主要拿 hashMap来举例
这里先来看看我们 常用的方法
put()方法 – 》设置 key 对应的 value
如果 有重复 元素 放入,value 会更新最后放入 的 元素的value 值
put存放 数据的 顺序问题
get() 方法 --》通过key 获取 value
public class Main {
public static void main(String[] args) {
Map<String,Integer> map = new HashMap<>();
map.put("小灰灰",3);
map.put("红太郎",2);
map.put("灰太狼",4);
System.out.println(map);
int ret = map.get("小灰灰"); 通过key 获取对应的 value
System.out.println(ret);
}
}
注意:有关 get() 返回 null
getOrDefault() 返回 key 对应的 value,key 不存在,返回默认值
public class Main {
public static void main(String[] args) {
Map<String,Integer> map = new HashMap<>();
map.put("小灰灰",3);
map.put("红太郎",2);
map.put("灰太狼",4);
System.out.println(map);
int ret = map.get("小灰灰");
// int sum = map.get("懒洋洋"); 拆包会发生null 异常
System.out.println(ret);
System.out.println(map.getOrDefault("懒洋洋",0));
// 如果 map中没有懒洋洋那么 返回默认值 0
}
}
remove() 删除 key 对应的映射关系
public static void main(String[] args) {
Map<String,Integer> map = new HashMap<>();
map.put("小灰灰",3);
Integer ret = map.remove("小灰灰");
System.out.println(ret);
System.out.println(map);
}
Set keySet() 返回所有 key 的不重复集合
public static void main(String[] args) {
Map<String,Integer> map = new HashMap<>();
map.put("小灰灰",3);
map.put("红太郎",2);
map.put("灰太狼",4);
map.put("小灰灰",2); // 这里 如果 有重复的key 那么 只会放一份key 到set 当中
System.out.println(map);
Set<String> set = map.keySet();
System.out.println(set);
}
注意: 这里 如果 有重复的key 那么 只会放一份key 到set 当中
Collection values() 返回所有 value 的可重复集合
public static void main(String[] args) {
Map<String,Integer> map = new HashMap<>();
map.put("小灰灰",3);
map.put("红太郎",2);
map.put("灰太狼",4);
map.put("小灰灰",2);
Collection<Integer> ret = map.values();
System.out.println(ret);
System.out.println(map);
}
Set<Map.Entry<K, V>> entrySet()
boolean containsKey(Object key)
public static void main(String[] args) {
Map<String,Integer> map = new HashMap<>();
map.put("小灰灰",3);
map.put("红太郎",2);
map.put("灰太狼",4);
boolean flg = map.containsKey("小灰灰");
System.out.println(flg);
boolean flg2 = map.containsKey("懒洋洋");
System.out.println(flg2);
}
boolean containsValue(Object value)
public static void main(String[] args) {
Map<String,Integer> map = new HashMap<>();
map.put("小灰灰",3);
map.put("红太郎",2);
map.put("灰太狼",4);
boolean flg = map.containsValue(3);
System.out.println(flg);
boolean flg2 = map.containsKey(44);
System.out.println(flg2);
}
注意事项
注意:
Map是一个接口,不能直接实例化对象,如果要实例化对象只能实例化其实现类TreeMap或者HashMap
Map中存放键值对的Key是唯一的,value是可以重复的
在Map中插入键值对时,key不能为空,否则就会抛NullPointerException异常,但是value可以为空
Map中的Key可以全部分离出来,存储到Set中来进行访问(因为Key不能重复)。
Map中的value可以全部分离出来,存储在Collection的任何一个子集合中(value可能有重复)。
Map中键值对的Key不能直接修改,value可以修改,如果要修改key,只能先将该key删除掉,然后再来进行
重新插入。TreeMap和HashMap的区别
set 的说明
Set与Map主要的不同有两点:Set是继承自Collection的接口类,Set中只存储了Key
特点存入 的 数据会去重
常用的 方法
这里 使用HashSet来演示
add() 添加元素,但重复元素不会被添加成功
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
set.add(2);
set.add(4);
// set 不会添加 相同的元素,会自动去重
set.add(2);
System.out.println(set);
}
Iterator iterator() 返回迭代器
通过迭代器来打印set 中的数据
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
set.add(2);
set.add(4);
// set 不会添加 相同的元素,会自动去重
set.add(3);
System.out.println(set);
Iterator<Integer> iterator = set.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
}
void clear() 清空集合
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
set.add(2);
set.add(4);
set.add(3);
System.out.println(set);
set.clear();
System.out.println(set);
}
boolean contains(Object o) 判断 o 是否在集合中
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
set.add(2);
set.add(4);
set.add(3);
boolean flg = set.contains(3);
System.out.println(flg);
}
boolean remove(Object o) 删除集合中的 o
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
set.add(2);
set.add(4);
set.add(3);
boolean flg = set.remove(3);
System.out.println(flg);
System.out.println(set);
}
int size() 返回set中元素的个数
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
set.add(2);
set.add(4);
set.add(3);
int size = set.size();
System.out.println(size);
}
boolean isEmpty() 检测set是否为空,空返回true,否则返回false
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
set.add(2);
set.add(4);
set.add(3);
System.out.println(set.isEmpty());
Set<Integer> set2 = new HashSet<>();
System.out.println(set2.isEmpty());
}
Object[] toArray() 将set中的元素转换为数组返回
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
set.add(2);
set.add(4);
set.add(3);
Object[] arr = set.toArray();
System.out.println(Arrays.toString(arr));
}
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(2);
list.add(4);
list.add(3);
Set<Integer> set = new HashSet<>();
set.add(2);
set.add(4);
set.add(3);
boolean flg = set.containsAll(list);
System.out.println(flg);
}
boolean containsAll(Collection<?> c)
boolean addAll(Collection<? extendsE> c)
set注意事项
- Set是继承自Collection的一个接口类
- Set中只存储了key,并且要求key一定要唯一
- Set的底层是使用Map来实现的,其使用key与Object的一个默认对象作为键值对插入到Map中的
- Set最大的功能就是对集合中的元素进行去重
- 实现Set接口的常用类有TreeSet和HashSet,还有一个LinkedHashSet,LinkedHashSet是在HashSet的基础
上维护了一个双向链表来记录元素的插入次序。- Set中的Key不能修改,如果要修改,先将原来的删除掉,然后再重新插入
- Set中不能插入null的key。
- TreeSet和HashSet的区别
HashSet 与 TreeSet 区别
知道了 map 和 set 的 方法,那么我们来做几个练习来熟练使用一下他们
1.给定10w 数据,统计每个数据出现的次数
附上代码
public static Map<Integer,Integer> func(int[] array) {
Map<Integer,Integer> map = new HashMap<>();
for(int x : array) {
// 没有 添加为 1
if(map.get(x) == null) {
map.put(x,1);
}else {
// 此时说明 map 中有 这个数字
// 可以 先获取 value 的值,然后在添加 1
int value = map.get(x);
// 根据map 添加重复的元素 会更新成最后一个。
map.put(x,value);
}
}
return map;
}
public static Map<Integer,Integer> func2(int[] array){
Map<Integer,Integer> map = new HashMap<>();
for(int x : array){
map.put(x,map.getOrDefault(x,0) + 1);
}
return map;
}
public static void main(String[] args) {
int[] array = new int[10_000];
Random random = new Random();
for(int i = 0;i<array.length;i++){
array[i] = random.nextInt(1000);
}
Map<Integer ,Integer> map = func(array);
System.out.println(map);
}
2.将10w个数据的重复的去重
我们刚刚学习了Set 是不是知道他的特点,不存在重复的元素,
那么是不是可以将这些元素放在set中,是不是就完成了去重操作。
public static Set<Integer> func3(int[] array) {
Set<Integer> set = new HashSet<>();
for(int x : array) {
set.add(x);
}
return set;
}
3.从10w个数据中,找到第一个重复的数据
每次把元素放到set里,放之前检查一下,set是不是已经有了
public static int func4(int[] array) {
Set<Integer> set = new HashSet<>();
for(int x : array) {
if(set.contains(x)) {
// 通过set 的 contains 来判断集合中是否含有x
//如果含有,那么此时为第一次重复的返回
return x;
}
set.add(x);
}
// 如果上面没有弹出那么说明此时,没有重复的元素
return -1;
}
上面 3 个 小练习 完成那么接下来我们上力扣 来完成几个练习题
只出现一次的数字
复制带随机指针的链表
这里通过 Map 完成 这里在补充 一种方法
附上代码
通过 Map 完成
public Node copyRandomList(Node head) {
Map<Node,Node> map = new HashMap<>();
Node cur = head;
while(cur != null){
// 遍历创建相同value的节点
Node prev = new Node(cur.val);
// 使用 map 来存储 cur 和 prev 对应的位置
map.put(cur,prev);
cur = cur.next;
}
cur = head;
while(cur != null){
// key 为 cur ,通过 key 找到 每次 存入的 value prev 创建的节点
map.get(cur).next = map.get(cur.next);
map.get(cur).random = map.get(cur.random);
cur = cur.next;
}
return map.get(head);
}
通过 迭代完成
public Node copyRandomList(Node head) {
for(Node p = head;p!= null; p = p.next.next){
Node q = new Node(p.val);
q.next = p.next;
p.next = q;
}
for(Node p = head;p != null;p = p.next.next) {
if(p.random != null) {
p.next.random = p.random.next;
}
}
Node cur = new Node(-1);
Node prev = cur;
for(Node p = head;p != null;p = p.next) {
Node q = p.next;
cur.next = q;
cur = q;
p.next = q.next;
}
return prev.next;
}
接下来继续
宝石与石头
附上代码
class Solution {
public int numJewelsInStones(String jewels, String stones) {
Set<Character> set = new HashSet<>();
for(char ch : jewels.toCharArray()) {
set.add(ch);
}
int count = 0;
for(int i = 0;i<stones.length();i++) {
if(set.contains(stones.charAt(i))) {
count++;
}
}
return count;
}
}
旧键盘
附上代码
public class Main {
// 旧键盘
public static void func(String str1, String str2) {
Set<Character> set1 = new HashSet<>();
for(char x : str2.toUpperCase().toCharArray()) {
// 将 键盘 有效的 值 添加到set1 当中
set1.add(x);
}
Set<Character> set2 = new HashSet<>();
for(char x : str1.toUpperCase().toCharArray()){
if(!set1.contains(x) && !set2.contains(x)) {
System.out.print(x);
set2.add(x);
}
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while(sc.hasNextLine()) {
String str1 = sc.nextLine();
String str2 = sc.nextLine();
func(str1,str2);
}
}
}
前K个高频单词
最后 写完 这道练习 题 map 和set 就 完了 加油
附上代码
class Solution {
// 前K个高频单词
public static List<String> topKFrequent(String[] words, int k) {
HashMap<String,Integer> map = new HashMap<>();
//1.统计每个单词 出现的次数
for(String s : words) {
if(map.get(s) == null) {
map.put(s,1);
}else {
int val = map.get(s);
map.put(s,val+1);
}
}
//2.使用topk 思路 创建要给小根堆 来求 前 个k 最大的
PriorityQueue<Map.Entry<String,Integer>> minHeap = new PriorityQueue<>(k, new Comparator<Map.Entry<String, Integer>>() {
@Override
public int compare(Map.Entry<String, Integer> o1, Map.Entry<String, Integer> o2) {
if(o1.getValue().compareTo(o2.getValue()) == 0) {
return o2.getKey().compareTo(o1.getKey());
}
return o1.getValue() - o2.getValue();
}
});
//3. 遍历 Map
for(Map.Entry<String,Integer> entry:map.entrySet()) {
// 此时 小根堆没有放满
if(minHeap.size() < k) {
minHeap.offer(entry);
}else {
// 此时 已经放满 了 k 个 元素, 此时 需要与堆顶元素比较,需要看堆顶元素的数据,和 当前的数据 大小关系
// 大于 堆顶元素 那么就弹出堆 顶元素 ,替换
Map.Entry<String,Integer> top = minHeap.peek();
// 判断频率是否相同,比较单词的大小,单词小的入堆
if(top.getValue().compareTo(entry.getValue()) == 0) {
// hellow 2 次 abc 也是 两次
//这里 需要看谁的 k 小,
if(top.getKey().compareTo(entry.getKey()) > 0) {
// 此时 堆顶的 k 会 大于 entry 所以 需要弹出
minHeap.poll();
minHeap.offer(entry);
}
}else {
// hellow 2 abc 4
if(top.getValue().compareTo(entry.getValue()) < 0) {
// entry 的频率大
minHeap.poll();
minHeap.offer(entry);
}
}
}
}
// System.out.println(minHeap);
List<String> ret = new ArrayList<>();
for(int i = 0;i < k;i++) {
Map.Entry<String,Integer> top = minHeap.poll();
ret.add(top.getKey());
}
// reverse 进行 降序操作 会 逆置 操作 ,会 将 a - 2 和 b - 2 转换 为 b - 2 和 a - 2
// 所以这里添加 一个比较条件
Collections.reverse(ret);
return ret;
// return null;
}
}