Java中Map和Set的常见用法举例
作者:權sir
一、Map和Set的概念
Map和set是一种专门用来进行搜索的容器或者数据结构,其搜索的效率与其具体的实例化子类有关,常见的搜索方式如下:
1.直接遍历,时间复杂度为O(N),元素如果比较多效率会非常慢
2.二分查找,时间复杂度为O(logN) ,但搜索前必须要求序列是有序的
上述排序比较适合静态类型的查找,即一般不会对区间进行插入和删除操作了,而现实中的查找比如:
1.根据姓名查询考试成绩
2.通讯录,即根据姓名查询联系方式
3.不重复集合,即需要先搜索关键字是否已经在集合中
可能在查找时进行一些插入和删除的操作,即动态查找,那上述两种方式就不太适合了,下面介绍的Map和Set是一种适合动态查找的集合容器
二、模型
一般把搜索的数据称为关键字(Key),和关键字对应的称为值(Value),将其称为Key-Value的键值对,所以模型有两种:
1.纯Key模型
有一个英文词典,快速查找一个单词是否在词典中
快速查找某个名字在不在通讯录中
2.Key-Value 模型
统计文件中每个单词出现的次数,统计结果是每个单词都有与其对应的次数:<单词,单词出现的次数>
梁山好汉的江湖绰号:每个好汉都有自己的江湖绰号
而Map中存储的就是key-value的键值对,Set中只存储了Key

三、Map的说明
Map是一个接口类,该类没有继承自Collection,该类中存储的是<K,V>结构的键值对,并且K一定是唯一的,不能重复
3.1 Map.Entry<K, V>的说明
Map.Entry<K, V> 是Map内部实现的用来存放<key, value>键值对映射关系的内部类,该内部类中主要提供了<key, value>的获取,value的设置以及Key的比较方式
| 方法 | 解释 |
|---|---|
| K getKey() | 返回entry中的key |
| V getValue() | 返回entry中的value |
| V setValue(V value) | 将键值对中的value替换为指定value |
注意:Map.Entry<K,V>并没有提供设置Key的方法
3.2 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< K > keySet() | 返回所有key的不重复集合 |
| Collection< V > values() | 返回所有value的可重复集合 |
| Set<Map.Entry<K,V>>entrySet() | 返回所有的key-value映射关系 |
| boolean containsKey(Object key) | 判断是否包含key |
| boolean containsValue(Object Value) | 判断是否包含value |
注意:
1.Map是一个接口,不能直接实例化对象,如果要实例化对象只能实例化其实现类TreeMap或者HashMap
2.Map中存放键值对的Key是唯一的,value是可以重复的
3.在TreeMap中插入键值对时,key不能为空,否则就会抛NullPointerException异常,value可以为空。但是HashMap的key和value都可以为空
4.Map中的Key可以全部分离出来,存储到Set中来进行访问(因为Key不能重复)
5.Map中的value可以全部分离出来,存储在Collection的任何一个子集合中(value可能有重复)
6.Map中键值对的Key不能直接修改,value可以修改,如果要修改key,只能先将该key删除掉,然后再来进行重新插入
四、Set的说明
Set与Map主要的不同有两点:Set是继承自Collection的接口类,Set中只存储了Key
4.1 Set的常用方法
| 方法 | 解释 |
|---|---|
| boolean add(E e) | 添加元素,但重复元素不会被添加成功 |
| void clear() | 清空集合 |
| boolean contains(Object o) | 判断o是否在集合中 |
| lterator< E > iterator() | 返回迭代器 |
| boolean remove(Object o) | 删除集合中的o |
| int size() | 返回set中元素的个数 |
| boolean isEmpty() | 检测set是否为空,空返回true,否则返回false |
| Object[] toArray() | 将set中的元素转换为数组返回 |
| boolean containsAll(Collection<?>c) | 集合c中的元素是否在set中全部存在,是返回true,否则返回false |
| boolean addAll(Conllection<? extends E>c) | 将集合c中的元素添加到set中,可以达到去重的效果 |
注意:
1.Set是继承自Collection的一个接口类
2.Set中只存储了key,并且要求key一定要唯一
3.TreeSet的底层是使用Map来实现的,其使用key与Object的一个默认对象作为键值对插入到Map中的
4.Set最大的功能就是对集合中的元素进行去重
5.实现Set接口的常用类有TreeSet和HashSet,还有一个LinkedHashSet,LinkedHashSet是在HashSet的基础上维护了一个双向链表来记录元素的插入次序
6.Set中的Key不能修改,如果要修改,先将原来的删除掉,然后再重新插入
7.TreeSet中不能插入null的key,HashSet可以
附:一些有关 Map 和 Set 的题目
1. 统计一组数据中的每个数据出现了多少次
/**
* 统计一组数据中的每个数据出现了多少次
*/
import java.util.HashMap;
import java.util.Map;
public class Test {
public static void main(String[] args) {
char[] arr = {'a','c','e','c','b','d','f','c','d'};
Map<Character,Integer> map = computer(arr);
System.out.println(map);
}
public static Map<Character,Integer> computer(char[] arr){
Map<Character,Integer> map = new HashMap<>();
//如果在 map 中没有数组对应的 Key,那么就添加 1个 Key 进去
//如果在 map 中包含了和数组对应的 Key,且 Key 对应 count个 Val,
//那么就添加 count + 1个 Key 进去
for (char x :arr) {
if(map.containsKey(x)){
int count = map.get(x);
map.put(x, count+1);
}else {
map.put(x,1);
}
}
return map;
}
}
2. 将一组数据中多余的数据去重
/**
* 将一组数据中多余的数据去重
*/
import java.util.HashSet;
import java.util.Set;
public class Test {
public static void main(String[] args) {
int[] arr = {1,3,5,7,9,3,2,4,6,8,5,4,6};
Set<Integer> set = new HashSet<>();
for (int x : arr) {
set.add(x);
}
System.out.println(set);
}
}
3. 找出第一个重复出现的数据
/**
* 在一组数据中,找出第一个重复出现的数据
*/
import java.util.HashSet;
import java.util.Set;
public class Test6 {
public static void main(String[] args) {
int[] arr = {1,3,5,7,9,3,2,4,6,8,5,4,6};
Set<Integer> set = new HashSet<>();
int i = 0;
//如果在添加元素到 set 之前,就已经发现了 set 中包含某个数组的元素,
//那么就 break
for (i = 0; i < arr.length; i++) {
if(set.contains(arr[i])){
break;
}
set.add(arr[i]);
}
System.out.println(arr[i]); //拿到的就是第一次出现的重复值
}
}
4. 只出现一次的数字
方法一:
/**
* 利用两两元素之间异或,最终得出的就是单独元素
*/
class Solution {
public int singleNumber(int[] nums) {
int x = nums[0];
for(int i= 1; i<nums.length; i++){
x = x^nums[i];
}
return x;
}
}
方法二:
/**
* 利用集合 Set
* 如果集合中有相同元素,就移除,反之,就添加
*/
class Solution {
public int singleNumber(int[] nums) {
Set<Integer> set = new HashSet<>();
for(int i=0; i<nums.length; i++){
if(!set.contains(nums[i])){
set.add(nums[i]);
}else{
set.remove(nums[i]);
}
}
for(int i=0; i<nums.length; i++){
if(set.contains(nums[i])){
return nums[i];
}
}
return -1;
}
}
5. 复制带随机指针的链表
本题需要详细掌握 Map 的操作,与其对应的映射关系,才能将本题理解到位。
/**
* 通过 Map 的映射关系
* 知道了 Key 的状态,实现 Value 的状态
* 即通过旧节点之间的关系,实现新节点之间的关系
*/
class Solution {
public Node copyRandomList(Node head) {
if(head == null) return null;
//Key - 旧节点,Value - 新节点
Map<Node,Node> map = new HashMap<>();
Node cur = head;
//1. 创建新的节点
while(cur != null){
map.put(cur, new Node(cur.val));
cur = cur.next;
}
cur = head;
//2. 连接各个节点
while(cur != null){
map.get(cur).next = map.get(cur.next);
map.get(cur).random = map.get(cur.random);
cur = cur.next;
}
return map.get(head);
}
}
6. 宝石与石头
方法一
/**
* 方法一
* 1. 将 jewels 字符串中的字符放入集合 set 中
* 2. 查看集合 set 中是否包含 stones 字符数组中字符
* 3. 用 count 计数,最后返回
*/
class Solution {
public int numJewelsInStones(String jewels, String stones) {
Set<Character> set = new HashSet<>();
for(char x:jewels.toCharArray()){ //1.
set.add(x);
}
int count = 0;
for(char j:stones.toCharArray()){ //2.
if(set.contains(j)){
count++;
}
}
return count; //3.
}
}
方法二
/**
* 方法二
* 两层 for 循环
*/
class Solution {
public int numJewelsInStones(String jewels, String stones) {
int count = 0;
for(int i=0; i<jewels.length(); i++){
for(int j=0; j<stones.length();j++){
if( jewels.charAt(i) == stones.charAt(j) ){
count++;
}
}
}
return count;
}
}
7. 键盘坏了的键
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
/**
* 1. 将实际输入的文字 str2 放在集合 set 中
* 2. for 循环遍历应该输入的文字 str1,
* 若集合 set 不包含某个字符,且线性表之前也未出现该字符,就输出打印
* 3. 将步骤 2 中不包含的字符放入顺序表中,以便下一次判断
*/
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String str1 = scanner.nextLine(); //应该输入的文字
String str2 = scanner.nextLine(); //实际输入的文字
out(str1,str2);
}
public static void out(String str1, String str2){
Set<Character> set = new HashSet<>();
ArrayList<Character> list = new ArrayList<>();
for (char x:str2.toUpperCase().toCharArray()) {
set.add(x); //1.
}
for(char x:str1.toUpperCase().toCharArray()){ //2.
if(!set.contains(x) && !list.contains(x)){
System.out.print(x);
}
list.add(x); //3.
}
}
}
8. 前K个高频单词
class Solution {
public List<String> topKFrequent(String[] words, int k) {
Map<String,Integer> hashMap =new HashMap<>();
for(String s: words) {
if(hashMap.get(s) == null) {
hashMap.put(s,1);
}else {
hashMap.put(s,hashMap.get(s)+1);
}
}
//2、建立小根堆
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().compareTo(o2.getValue());
}
}
);
//3、遍历hashMap 把里面 的数据 放到小根堆
for(Map.Entry<String,Integer> entry : hashMap.entrySet()) {
if(minHeap.size() < k) {
minHeap.offer(entry);
}else {
//小根堆放满了K个,下一个entry和堆顶元素比较
Map.Entry<String,Integer> top = minHeap.peek();
//堆顶的频率小于当前entry的频率 就出队 然后入队entry
if(top.getValue().compareTo(entry.getValue()) < 0) {
minHeap.poll();
minHeap.add(entry);
}else {
//频率相同的情况
if(top.getValue().compareTo(entry.getValue()) == 0) {
if(top.getKey().compareTo(entry.getKey()) > 0) {
minHeap.poll();
minHeap.add(entry);
}
}
}
}
}
//4、 此时小根堆当中已经有了结果
//System.out.println(minHeap);
List<String> ret = new ArrayList<>();
for (int i = 0; i < k; i++) {
String key = minHeap.poll().getKey();
ret.add(key);
}
Collections.reverse(ret);
//System.out.println("ret: "+ret);
return ret;
}
}总结
到此这篇关于Java中Map和Set的常见用法举例的文章就介绍到这了,更多相关Java Map和Set内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
