Map 和 Set

简介: Map 和 Set

引言



Map 和 Set 是一种专门用来进行搜索的容器或者数据结构,其搜索的效率与其具体的实例化子类有关。


一、Map 接口



Map是一个接口,该接口并没有继承(拓展)Collection接口。Map 接口中存储的是<K,V>结构的键值对,将键 Key 映射到值 Value 的对象。 此外,Map 中不能包含重复的键,每个键可以映射到最多一个值。


Map<K,V>
//Key ——键
//Value ——值

53b692cfe7254fc299fc786792f3c1e8.jpg


1. Map 的常用方法


73a44c13b1144d099d48a5dac1273671.png


2. 使用 Map 时的说明


① Map 是一个接口,所以不能直接通过 new 来实例化对象,如果要实例化对象,那么只能实例化其实现类 TreeMap 或者 HashMap.

② Map 中存放键值对的 Key 是唯一的,Value 是可以重复的。而重复的 Key 对应的 Value 会在底层被更新。

③ Map中的 Key 可以全部分离出来,存储到Set中来进行访问(因为 Key 不能重复)。

④ Map 中的 Value 可以全部分离出来,存储在 Collection 的任何一个子集合中(Value 可能有重复)。

⑤ Map中键值对的 Key 不能直接修改,Value 可以修改,如果要修改 Key ,只能先将该 Key 删除掉,然后再来进行重新插入。

⑥ TreeMap 和 HashMap的区别:


10d096ebba5245faa1cf547678c97fc1.png


3. Java 中 Map 的使用


/**
 * Map 接口
 */
import java.util.HashMap;
import java.util.Map;
public class Test1 {
    public static void main(String[] args) {
        //Map<K,V> key,value
        Map<String, Integer> map = new HashMap<>();
        map.put("hello", 3);
        map.put("world",4);
        map.put("nice",5);
        map.put("nice",666);//重复的 Key 对应的 Val 会更新
        map.put(null, null);
        System.out.println(map);
        int ret = map.get("world");//获得当前 Key 在 map 中的次数
        System.out.println(map.get("world"));
        System.out.println(map.get("world2"));
        System.out.println(map.getOrDefault("world3",-1));
        map.remove("world");
        System.out.println(map);
    }
}


输出结果:


1f91904153924684b967a8372221f08a.png


4. 注意


在上面我们使用 map.put( ) 方法的时候,我们会发现一个问题,我们 put 进去 map 的数据顺序是 [hello, world, nice, null ],但是输出的却是 [ null, world, hello, nice ],可以发现,我们从输出数据的顺序上来看,这很糟糕。

但是我在引言中提到,Map 是一种专门用来进行搜索的容器或者数据结构,其核心在于查找,并不是排序。也就是说,我们在使用 Map 和 Set 时,更多地是思考用来查找、统计数据,而对数据的顺序并不关心。


/**
 * Map 接口
 */
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Test2 {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("hello", 3);
        map.put("world",4);
        map.put("nice",5);
        //此时 Set 集合中放的数据类型是: String
        //Set 这个集合中,存储的元素都是不重复的
        Set<String> set = map.keySet();
        System.out.println(set);
        //Set 集合中放的数据类型是: Map.Entry<String, Integer>
        //所以此时 Set 集合中放的就是 String 和 Integer 的集合体
        Set<Map.Entry<String, Integer>> entrySet = map.entrySet();
        for (Map.Entry<String, Integer> s : entrySet) {
            System.out.println(s.getKey()+" -> "+ s.getValue() );
        }
    }
}


输出结果:


99b3a7ad4b4b47fb8c2257d6374c5abf.png


二、Set 接口



Set 接口与 Map 接口主要有两点不同:


① Set 接口是继承了 (拓展) Collection 的接口

② Set 中只存储了 Key


Set<K>
//K —— 键


1. Set 的常用方法


5626cde9373b4f919b29ad6bf3befe24.png


2. 使用 Set 时的说明


① Set 接口是继承了 (拓展) Collection 的接口。

Set 中只存储了 Key ,并且要求 Key 一定要唯一。

③ Set 的底层是使用 Map 来实现的,其使用 Key 与 Object 的一个默认对象作为键值对插入到 Map 中的。

④ Set 最大的功能就是对集合中的元素进行去重。

⑤ 实现 Set 接口的常用类有 TreeSet 和 HashSet,还有一个 LinkedHashSet,LinkedHashSet 是在 HashSet 的基础上维护了一个双向链表来记录元素的插入次序。

⑥ Set 中的 Key 不能修改,如果要修改,先将原来的删除掉,然后再重新插入。

⑦ Set 中不能插入 null 的 Key 。

⑧ TreeMap 和 HashMap 的区别:


11d6548b07624d0fb88196bf9122e205.png


3. Java 中 Set 的使用


import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class Test2 {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        set.add("hello");
        set.add("world");
        set.add("nice");
        set.add("nice");
        System.out.println(set);
        Iterator<String> iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}


输出结果:


a9eda3e7b17a411581e0974a46d3d0d6.png


4. 注意


在上面我们使用 set.add( ) 方法的时候,我们会发现,当添加重复的元素时,集合会自动帮我们去除重复的元素。此外,我们输出的元素会和 Map 一样,并不是按我们输入的顺序排序好的。因为我在引言中提到,同样地,Set 也只是一个用于搜索、查找的数据结构。


三、一些有关 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;
    }
}


输出结果:


274a632ac9bd4c538829ef4429449275.png


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);
    }
}


输出结果:


d14ef097112b4a7a824db0cb47791160.png


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. 只出现一次的数字


leetcode 136


方法一:


/**
 * 利用两两元素之间异或,最终得出的就是单独元素
 */
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. 复制带随机指针的链表


leetcode 138


本题需要详细掌握 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. 宝石与石头


leetcode 771


方法一


/**
 * 方法一
 * 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.
        }
    }
}


目录
相关文章
|
1月前
|
算法
你对Collection中Set、List、Map理解?
你对Collection中Set、List、Map理解?
73 18
你对Collection中Set、List、Map理解?
|
1月前
|
存储 缓存 安全
只会“有序无序”?面试官嫌弃的List、Set、Map回答!
小米,一位热衷于技术分享的程序员,通过与朋友小林的对话,详细解析了Java面试中常见的List、Set、Map三者之间的区别,不仅涵盖了它们的基本特性,还深入探讨了各自的实现原理及应用场景,帮助面试者更好地准备相关问题。
65 20
|
2月前
|
存储 C++ 容器
【C++】map、set基本用法
本文介绍了C++ STL中的`map`和`set`两种关联容器。`map`用于存储键值对,每个键唯一;而`set`存储唯一元素,不包含值。两者均基于红黑树实现,支持高效的查找、插入和删除操作。文中详细列举了它们的构造方法、迭代器、容量检查、元素修改等常用接口,并简要对比了`map`与`set`的主要差异。此外,还介绍了允许重复元素的`multiset`和`multimap`。
45 3
【C++】map、set基本用法
|
2月前
|
存储 算法 C++
【C++】unordered_map(set)
C++中的`unordered`容器(如`std::unordered_set`、`std::unordered_map`)基于哈希表实现,提供高效的查找、插入和删除操作。哈希表通过哈希函数将元素映射到特定的“桶”中,每个桶可存储一个或多个元素,以处理哈希冲突。主要组成部分包括哈希表、哈希函数、冲突处理机制、负载因子和再散列,以及迭代器。哈希函数用于计算元素的哈希值,冲突通过开链法解决,负载因子控制哈希表的扩展。迭代器支持遍历容器中的元素。`unordered_map`和`unordered_set`的插入、查找和删除操作在理想情况下时间复杂度为O(1),但在冲突较多时可能退化为O(n)。
32 5
|
4月前
|
存储 Java API
【数据结构】map&set详解
本文详细介绍了Java集合框架中的Set系列和Map系列集合。Set系列包括HashSet(哈希表实现,无序且元素唯一)、LinkedHashSet(保持插入顺序的HashSet)、TreeSet(红黑树实现,自动排序)。Map系列为双列集合,键值一一对应,键不可重复,值可重复。文章还介绍了HashMap、LinkedHashMap、TreeMap的具体实现与应用场景,并提供了面试题示例,如随机链表复制、宝石与石头、前K个高频单词等问题的解决方案。
56 6
【数据结构】map&set详解
|
3月前
|
存储 JavaScript 前端开发
Set、Map、WeakSet 和 WeakMap 的区别
在 JavaScript 中,Set 和 Map 用于存储唯一值和键值对,支持多种操作方法,如添加、删除和检查元素。WeakSet 和 WeakMap 则存储弱引用的对象,有助于防止内存泄漏,适合特定场景使用。
|
3月前
|
存储 缓存 Java
【用Java学习数据结构系列】HashMap与TreeMap的区别,以及Map与Set的关系
【用Java学习数据结构系列】HashMap与TreeMap的区别,以及Map与Set的关系
52 1
|
4月前
|
算法
你对Collection中Set、List、Map理解?
你对Collection中Set、List、Map理解?
48 5
|
4月前
|
存储 JavaScript 前端开发
js的map和set |21
js的map和set |21
|
4月前
|
存储 前端开发 API
ES6的Set和Map你都知道吗?一文了解集合和字典在前端中的应用
该文章详细介绍了ES6中Set和Map数据结构的特性和使用方法,并探讨了它们在前端开发中的具体应用,包括如何利用这些数据结构来解决常见的编程问题。
ES6的Set和Map你都知道吗?一文了解集合和字典在前端中的应用