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.
        }
    }
}


目录
相关文章
|
6天前
|
存储 JavaScript 索引
js开发:请解释什么是ES6的Map和Set,以及它们与普通对象和数组的区别。
ES6引入了Map和Set数据结构。Map的键可以是任意类型且有序,与对象的字符串或符号键不同;Set存储唯一值,无重复。两者皆可迭代,支持for...of循环。Map有get、set、has、delete等方法,Set有add、delete、has方法。示例展示了Map和Set的基本操作。
24 3
|
6天前
|
存储 数据格式
Set和Map的应用场景
Set和Map的应用场景
|
6天前
|
存储 自然语言处理 C++
map和set的简单介绍
map和set的简单介绍
26 1
|
4天前
|
JavaScript 前端开发 Java
ES6 逐点突破系列 -- Set Map,工作感悟,完美收官
ES6 逐点突破系列 -- Set Map,工作感悟,完美收官
|
4天前
|
存储 缓存 JavaScript
JavaScript中的Set和Map:理解与使用
JavaScript中的Set和Map:理解与使用
|
6天前
|
存储 JavaScript
ES6+新特性-Symbol与Set/Map数据结构
ES6 引入了三种新的数据结构:Symbol、Set和Map。Symbol是唯一且不可变的值,常用于定义对象的独特属性;Set存储不重复值,适合数组去重;Map则是键值对集合,键可为任意类型,提供了更灵活的存储方式。这些新数据结构提供了更高效的操作手段,分别解决了属性命名冲突、数据去重和复杂键值对存储的问题。示例展示了如何使用Symbol、Set和Map进行基本操作。
|
6天前
|
存储 编译器 C++
C++:map&set 对红黑树的封装
C++:map&set 对红黑树的封装
11 1
|
6天前
|
存储
Map与Set的经典OJ题
Map与Set的经典OJ题
13 3
|
6天前
|
存储 自然语言处理 容器
Map与Set
Map与Set
13 3
|
6天前
|
存储 C++ 容器
C++:STL - set & map
C++:STL - set & map
16 4