HashSet与HashMap

本文主要内容:

  • Set:HashSet、TreeSet
  • Map:HashMap、TreeMap
  • Set及Map的应用
    1.找出只出现一次的元素
    2.复制带随机指针的链表
    3.宝石与石头
    4.坏键盘打字,找出坏的键
    5.前k个高频单词
    6.子域名访问计数

集合:Collection(以value的形式存在):List 、Queue 、Set 、 Deque;Map(以key-value的形式存在)

  • Set:无序、无重复

**无序:**添加的顺序与获取的顺序不一致(不是集合本身是否有序,Tree自然有序)
**无重复:**添加的元素不能一致(如果出现重复的元素,只存第一个,不在存入)
HashSet(HashMap—>数据存储结构:散列表)
TreeSet(TreeMap—>数据存储结构:二叉树)

Set集合家族基本使用:
add(E e)、remove(E e)、 contains:是否包含
Iterator迭代器(增强for)、 size();

public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
//Set源码中的add方法,实际上用的是Map中的put方法,且将它当做key传入
//所以set无重复,map中的key是无重复的

Set集合无重复的特性:
**HashSet:**无重复的原则有两个方法同时起作用:equals、hashCode,默认比较的是两个对象的地址,若第二个对象地址与之前的一致,不在存入,如果想要改变其比较的规则,可以重写上述的两个方法。
**TreeSet:**无重复的原则只有一个方法起作用:compareTo,上述的方法不是每个对象都有的,若想将某一个对象存入TreeSet集合中,需要让对象所属的类的接口Comparable,实现接口后将ComparatorTo方法重写,返回int,负数靠前排布,正数排列靠后。(有序)

  • Map:映射–>通过一个key可以直接定位到一个value值

存储方式:以键值对的形式存储(key-value)
注意:key:无序无重复 value:无序可重复
key无序还是一样,指存入顺序与取得顺序不一致,key无重复指的是元素不能一致

基本方法:增删改查
**增:**put(key,value):存放一组映射关系(key-value)
(1)key添加的顺序与取得的顺序不同
(2)不同的key可以存储相同的value
(3)如果key若是相同,则将原有的value覆盖而不是拒绝存入(跟set刚好相反)
**删:**remove(key);
**改:**put(key,value1); put(key,value2);通过覆盖的形式进行修改
replace(key,newValue)
**查:**get(key)
**遍历集合:**获取到所有的key,遍历key,通过key获取value
其他API中提供的方法:
clear()、containsKey(key)、containsValue(value)、getOrDefault(key,defaultValue):如果key存在就返回对应的value,若没有找到,则返回默认值。
isEmpty()、putAll(map)、putIfAbsent(key,value):如果key不存在才向集合中添加元素,如果key存在就不添加了
HashMap:数据存储方式:散列表(数组+链表)

//计算哈希码
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

注意:不同的对象可以产生相同的hashCode码,不同的hashCode码应该对应不同的对象,数组里存放的是对象的HansCode码,如果发现hashCode码一致,就在当前数组下标后面串一个Node。数组存放的是Hash值,将Node{key+value}包装成一个对象Entry:Map.Entry
TreeMap:数据存储方式:二叉树(红黑树)
**自然有序:**按照UniCode编码自然有序,map集合中的key需要可比较的,key的对象需要实现Comparable接口。在构建树时,每一次的对象与之前的entry作比较(compareTo),如果小于就放左边,如果大于就放右边,如果一致就覆盖,注意:红黑二叉树层级多余两层,左旋、右旋

  • 在做题时,应该采用数组还是集合呢?

1.数组or集合:如果存储的元素长度不变就用数组,如果长度不确定就用集合
2.长度不确定——集合
List:存储有顺序
ArrayList:更适合遍历轮询
LinkedList:更适合插入删除
Stack:先进后出
Set家族无重复:存储的元素希望自动去掉重复的元素
Hash:性能更高
Tree:希望进去的元素自动去重复,同时还能自动排序
Map家族k-v:通过唯一的k快速寻找v
Hash:性能更高
Tree:希望存进去的元素key自动排序

  • 代码实现应用题
class Node{
    int value;
    Node next;
    Node random;
    public Node(int value){
        this.value = value;
    }
}
public class Solution {
    //数组中只有一个元素出现了一次,其余元素出现了两次,找出那个只出现一次的元素
    public static int singleNumber(int[] number) {
        //首先定义一个map记录元素与其出现的次数,然后通过次数找到那个出现一次的元素
        Map<Integer,Integer> map = new HashMap<>();
        for(int num:number){
            int count = map.getOrDefault(num,0);
            map.put(num,count+1);
        }
        int keyResult = 0;
        for(Map.Entry<Integer,Integer> entry: map.entrySet()){
            int key = entry.getKey();
            int value = entry.getValue();
            if(value==1){
                keyResult = key;
            }
        }
        return keyResult;
    }

    public static Node createNode(){
        Node n1 = new Node(7);
        Node n2 = new Node(3);
        Node n3 = new Node(5);
        Node n4 = new Node(2);
        n1.next = n2;n2.next = n3;n3.next = n4;n4.next = null;
        n1.random = n3;n2.random = n1;n3.random = n3;n4.random = null;
        return n1;
    }

    public static void show(Node head){
        Node cur = head;
        while (cur!=null){
            System.out.format("%d-->",cur.value);
            cur = cur.next;
        }
        System.out.println("null");
    }

    //复制带随机指针的链表
    public static Node copyRandomList1(Node head) {
        //首先将每个结点都复制一份,插入到链表中每个结点的后面,构成一条新链表,通过原链表的random的next找到新链表的random,最后拆分链表
        if(head==null){
            return null;
        }
        Node cur = head;
        while (cur!=null){
            Node node = new Node(cur.value);
            node.next = cur.next;
            cur.next = node;
            cur = cur.next.next;
        }
        cur = head;
        Node newNode = cur.next;
        while (cur!=null){
            if(cur.random == null){
                newNode.random = null;
            }else {
                newNode.random = cur.random.next;
            }
            cur = cur.next.next;
        }
        Node result = head.next;
        cur = head;
        while (cur!=null){
            cur.next = newNode.next;
            if(newNode.next!=null){
                newNode.next = newNode.next.next;
            }
            cur = cur.next;
        }
        return result;
    }


    public static Node copyRandomList2(Node head) {
        //首先将原有的链表复制(除random外)一份,得到一个与原先链表(无random)一样的链表,然后将两个链表的值分别放入map中,新链表的random是旧链表random的value
        Map<Node,Node> map = new HashMap<>();
        Node cur = head;
        Node result = null;
        Node last = null;
        while (cur!=null){
            Node node = new Node(cur.value);
            if(result==null){
                result = node;
            }else {
                last.next = node;
            }
            last = node;
            map.put(cur,node);
            cur = cur.next;
        }
        cur = head;
        Node n = result;
        while (cur!=null){
            //cur的random的值就是n的random
            n.random = map.get(cur.random);
            cur = cur.next;
            n = n.next;
        }
        return result;
    }
    //J代表宝石类型,S代表你拥有的石头,S中每个字符代表一种石头,返回S中有多少种宝石
    public static int numJewelsInStones(String J, String S) {
        //将J中的元素放入set中(不重复),然后遍历S,看set中是否包含S中的元素,如果有,count加一
        //数组a为宝石,数组b为石头
        char[] a = S.toCharArray();
        Set<Character> set = new HashSet<>();
        for(char ch:a){
            set.add(ch);
        }
        int count = 0;
        char[] b = J.toCharArray();
        for(char ch:b){
            if(set.contains(ch)){
                count++;
            }
        }
        return count;
    }

    public static void main(String[] args) {
        int[] number = {2,1,2,1,3,3,4,5,5,6,6};
        int keyResult = singleNumber(number);
        System.out.println(keyResult);
        System.out.println("===============================");
        Node node1 = createNode();
        copyRandomList1(node1);
        show(node1);
        System.out.println("===============================");
        Scanner scanner = new Scanner(System.in);
        String J = scanner.nextLine();
        String S = scanner.nextLine();
        int baoShiResult = numJewelsInStones(J,S);
        System.out.println(baoShiResult);
        System.out.println("===============================");
        //键盘中有一些键坏了,已知输入的数据以及要输进去的数据,进行对比,将坏掉的键以顺序且大写打印出来
        Scanner sc = new Scanner(System.in);
        String actual = sc.nextLine();
        String now = sc.nextLine();
        //now数组且元素都大写
        char[] a = now.toUpperCase().toCharArray();
        Set<Character> set = new HashSet<>();
        for(char ch:a){
            set.add(ch);
        }
        //actual数组且数组大写
        char[] b = actual.toUpperCase().toCharArray();
        Set<Character> brokenKeys = new HashSet<>();
        for(char ch:b){
            if(!set.contains(ch)){
                if(!brokenKeys.contains(ch)){
                    System.out.print(ch+" ");
                    brokenKeys.add(ch);
                }
            }
        }
    }
}
//运行结果及截屏
4
===============================
7-->3-->3-->5-->5-->2-->2-->null
===============================
aAb
AAABBBa
2
===============================
String_beatiful_wanMei
Strng_beatful_wanMe
I 

复制带随机指针的链表运行结果截屏:

//前k个高频单词
public class Solution1 {

    public static class stringComparator implements Comparator<String>{
        @Override
        public int compare(String o1, String o2) {
            return o1.compareTo(o2);
        }
    }
    public static Map<String, Integer> count(String[] words) {
        //首先定义一个map,里面数组中的元素及它要出现的次数
        Map<String,Integer> map = new HashMap<>();
        for(String s: words){
            int count = map.getOrDefault(s,0);
            map.put(s,count+1);
        }
        return map;
    }

    public static Map<Integer, List<String>> remap(Map<String, Integer> wordToCount) {
        //在定义一个remap,里面包含出现相同的次数以及线性表(线性表中装的是出现次数相同的元素)
        Map<Integer,List<String>> remap = new HashMap<>();
        for(Map.Entry<String,Integer> entry:wordToCount.entrySet()){
            String keys = entry.getKey();
            int values = entry.getValue();
            List<String> list = remap.get(values);
            if(list==null){
                list = new ArrayList<>();
                remap.put(values,list);
            }
            list.add(keys);
        }
        return remap;
    }

    public static List<String> topKFrequent(String[] words, int k) {
        //将remap的key放入到一个数组中去,然后对其排序,这样就可以得到次数最高的
        Map<String,Integer> wordToCount = count(words);
        Map<Integer,List<String>> countToList = remap(wordToCount);
        Set<Integer> counts = countToList.keySet();
        int[] count = new int[counts.size()];
        int i =0;
        for(int key:counts){
            count[i++] = key;
        }
        Arrays.sort(count);
        //首先定义一个list放入结果元素,定义j=0,当j<k时,数组从后往前取元素,通过remap得到此次数的集合,然后对其进行排序
        //判断k-j与集合size作比较,如果小于,则全部加到list中,如果大于截取list后,将元素添加到list中,最后返回list。
        List<String> result = new ArrayList<>();
        Comparator<String> comparator = new stringComparator();
        int j = 0;
        int index = count.length-1;
        while (j<k){
            int c = count[index--];
            List<String> wordList = countToList.get(c);
            wordList.sort(comparator);
            if(wordList.size()<=k-j){
                result.addAll(wordList);
                j+=wordList.size();
            }else {
                result.addAll(wordList.subList(0,k-j));
                j = k;
            }
        }
        return result;
    }
    public static void main(String[] args) {
        String[] words = {
                "i", "love", "leetcode",
                "i", "love", "coding"
        };
        List<String> r = topKFrequent(words, 3);
        System.out.println(r);
    }
}
//运行结果
[i, love, coding]
//子域名访问计数
public class Solution3 {
    //900 google.mail.com-->返回结果900 google.mail.com,900 mail.com,900 com
    public static List<String> subDoMainVisits(String[] cpDomMains) {
        //首先遍历数组,将所有的元素以空格分开,然后将后面的元素以点分开,接着遍历数组,利用拷贝数组将其组合起来
        //定义一个map,里面包含s与次数,最后遍历map,将元素添加到list中
        Map<String,Integer> map = new HashMap<>();
        List<String> list = new ArrayList<>();
        for(String s:cpDomMains){
            String[] a = s.split(" ");
            int number = Integer.parseInt(a[0]);//将字符串转换为int类型
            String[] b =a[1].split("\\.");
            for(int i = 0;i<b.length;i++){
                String[] c = Arrays.copyOfRange(b,i,b.length);//注意截取时
                String result = String.join(".",c);
                int numbers = map.getOrDefault(result,0);
                map.put(result,numbers+number);
            }
        }
        for(Map.Entry<String,Integer> entry:map.entrySet()){
            String keys = entry.getKey();
            int values = entry.getValue();
            list.add(values+" "+keys);
        }
        return list;
    }

    public static void main(String[] args) {
        String[] s = { "900 google.mail.com", "50 yahoo.com", "1 intel.mail.com", "5 wiki.org" };
        List<String> r = subDoMainVisits(s);
        System.out.println(r);
    }
}
//运行结果
[951 com, 900 google.mail.com, 1 intel.mail.com, 5 org, 5 wiki.org, 901 mail.com, 50 yahoo.com]
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值