Map 和 Set

引言

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

一、Map 接口

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

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

1

1. Map 的常用方法

out

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的区别:

1

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

输出结果:

out

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

输出结果:
out

二、Set 接口

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

① Set 接口是继承了 (拓展) Collection 的接口
② Set 中只存储了 Key

Set<K>
//K —— 键

1. Set 的常用方法

out

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 的区别:
2

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

输出结果:

out

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

输出结果:

1

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

输出结果:

2

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.
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十七ing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值