数据结构之Map/Set讲解+硬核源码剖析(二)

简介: 数据结构之Map/Set讲解+硬核源码剖析(二)

数据结构之Map/Set讲解+硬核源码剖析(一)+https://developer.aliyun.com/article/1413569

get和getOrDefault的源码
// get也可以用来判断是否包含相应的key
        public V get(Object key) {
            TreeMap.Entry<K,V> p = getEntry(key);
            return (p==null ? null : p.value);
        }
        default V getOrDefault(Object key, V defaultValue) {
            V v;  // 三目运算符   为真返回v  为假返回默认值
            return (((v = get(key)) != null) || containsKey(key))
                    ? v
                    : defaultValue;
        }

3.remove

 删除key对应的映射关系

源码:注意remove存在返回值

// remove存在返回值!!!  返回你要删除的key对应的value
        public V remove(Object key) {
            TreeMap.Entry<K,V> p = getEntry(key);
            if (p == null)
                return null;
            V oldValue = p.value;
            deleteEntry(p);
            return oldValue;
        }

验证

System.out.println(map.remove("cat"));// 输出17
        System.out.println(map.remove("Dog"));// 输出null

4.contains  

包含两个contains方法,一个是判断是否存在key,一个判断是否存在value

System.out.println(map.containsKey("cat"));// 输出true
        System.out.println(map.containsKey("Dog"));// 输出false
        System.out.println(map.containsValue(15));// 输出true
        System.out.println(map.containsValue(100));// 输出false

源码

public boolean containsKey(Object key) {
        return getEntry(key) != null;
    }
    public boolean containsValue(Object value) {
        for (Entry<K,V> e = getFirstEntry(); e != null; e = successor(e))
            if (valEquals(value, e.value))
                return true;
        return false;
    }

5.keySet方法

  返回不重复的key的集合  就是将Map中所有的key值存放到Set内部(存的时候会进行排序)

// 此处Set里面存放的类型要和key一致!!!
        Set<String> set = map.keySet();
        for (String s:set) {
            System.out.print(s + " ");// 输出apple bank cat
        }

源码

public Set<K> keySet() {
        return navigableKeySet();
    }
    /**
     * @since 1.6
     */
    public NavigableSet<K> navigableKeySet() {
        KeySet<K> nks = navigableKeySet;
        return (nks != null) ? nks : (navigableKeySet = new KeySet<>(this));
    }

6.values

 返回所有的value的可重复集合  和上一个方法类似  此方法是拿到所有的value将其存放到Collection里面

Collection<Integer> collection = map.values();
        for (int val:collection) {
            System.out.print(val + " ");// 输出14 15 17 
        }

源码

public Collection<V> values() {
        Collection<V> vs = values;
        if (vs == null) {
            vs = new Values();
            values = vs;
        }
        return vs;
    }

7.entrySet方法

返回所有的key--value的映射关系  可以理解为keySet和values的集合版本

Set<Map.Entry<String,Integer>> set = map.entrySet();
        for (Map.Entry<String,Integer> entry:set) {
            System.out.println("key = " + entry.getKey() + " " +"value = " + entry.getValue());
        }
        // 也可以直接打印
        System.out.println(set); // 输出[apple=14, bank=15, cat=17]

注意:getKey和getValue是Map的内部类Entry中的方法  用于返回映射中的key和value,所以entrySet方法就相当于创建的Set集合中存储是一个一个Entry

注意:

1.Map是一个接口,只能实例化实现他的类,如TreeMap和HashMap,区别在于前者的底层是二叉搜索树,后者的底层是哈希表

2.Map中存放的key是唯一的(二叉搜索树中不能存放相同的值,哈希表中也不能存放相同的值)

3.在TreeMap中插入键值对时,key不能为空,否则就会抛NullPointerException异常,因为每次put一个元素都需要进行一次比较,而null不能直接比较,value可以为空。但 是HashMap的key和value都可以为空

4.利用keySet方法可以将所有的key分离出来(存放到set里),利用values方法可以将所有的value分离出来(存放到Collection内部)

5.Map中的key不能直接修改,value可以修改(会直接被替换为新值,在二叉搜索树的put代码中就有)

四.set的使用

1.概念

 Set是一种纯key模型的数据集合容器 Set是继承自Collection的接口类,也就是Set中不存在values

2.常用方法

3.注意事项:

1.TreeSet的底层其实是TreeMap,所以TreeSet无法存放相同的key值

2.Set实现了toString方法,可以直接打印

Set<String> set = new TreeSet<>();
        set.add("lvzi");
        set.add("biandu");
        set.add("zhizi");
        System.out.println(set);// 输出[biandu, lvzi, zhizi]

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

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

5.TreeSet中不能插入null的key,HashSet可以

五.哈希表

1.概念

1.什么是哈希表

 在顺序结构和平衡二叉树的存储结构中,如果想要搜索某个元素,必须要进行关键码(key)的多次比较,顺序结构的搜索效率是O(N),平衡二叉树的搜索效率是O(logN),搜索的效率取决于比较的次数,,其实之所以需要比较是因为关键码(key)和值(val)之间没有一一对应的关系,如果建立一种关键码与存储位置一一对应的映射,那么我们就能不进行比较就拿到我们需要的数据。比如,,对于一个正比例函数,x与y之间是一一对应的,可以说y与x建立了一一对应的映射关系。

 在计算机中,也有这样的一种数据结构,叫做哈希表,实现了关键码和存储位置的一一映射

这种映射关系通过哈希函数(hashFunc)建立(就像正比例函数建立x与y的映射关系一样)

比如:哈希函数设置为:hash(key) = key % capacity; capacity为存储元素底层空间总的大小。

2.哈希冲突

 如上图,如果我们再存储一个14,14%10 = 4,他的索引应该是4,但是4下标对应关键字key,不能再对应14了,此时就发生了哈希冲突

3.负载因子

α = 填入表中的元素 / 哈希表的长度

负载因子α用于定性描述冲突率,研究表明,负载因子越大,发生哈希冲突的概率就越大;负载因子越小,发生哈希冲突的概率就越小

2.常见的哈希函数

1.直接定制法

 利用关键字的特性建立线性函数,实现关键字和存储位置的一一对应!

适用于数据简单,连续的情况

相关面试题:

387. 字符串中的第一个唯一字符 - 力扣(LeetCode)

思路分析

代码实现

// 1.使用计数数组
class Solution {
    public int firstUniqChar(String s) {
        int[] cnt = new int[26];
        for (int i = 0; i < s.length(); i++) {
            cnt[s.charAt(i) - 'a']++;
        }
        for (int i = 0; i < s.length(); i++) {
            if(cnt[s.charAt(i) - 'a'] == 1) {
                return i;
            }
        }
        return -1;
    }
}
        2.// 使用哈希表
        Map<Character,Integer> map = new HashMap<>();
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);
            // 先获取ch的val  无论是否存在  是要val+1  不存在 设置默认值为0 更新后变为1
            map.put(ch,map.getOrDefault(ch,0)+1);
        }
        for (int i = 0; i < s.length(); i++) {
            if(map.get(s.charAt(i)) == 1) {
                return i;
            }
        }
        return -1;

数据结构之Map/Set讲解+硬核源码剖析(三)+https://developer.aliyun.com/article/1413573


目录
相关文章
|
3月前
|
存储 缓存 JavaScript
Set和Map有什么区别?
Set和Map有什么区别?
267 1
|
4月前
|
存储 JavaScript 前端开发
for...of循环在遍历Set和Map时的注意事项有哪些?
for...of循环在遍历Set和Map时的注意事项有哪些?
265 121
|
7月前
|
编译器 C++ 容器
【c++丨STL】基于红黑树模拟实现set和map(附源码)
本文基于红黑树的实现,模拟了STL中的`set`和`map`容器。通过封装同一棵红黑树并进行适配修改,实现了两种容器的功能。主要步骤包括:1) 修改红黑树节点结构以支持不同数据类型;2) 使用仿函数适配键值比较逻辑;3) 实现双向迭代器支持遍历操作;4) 封装`insert`、`find`等接口,并为`map`实现`operator[]`。最终,通过测试代码验证了功能的正确性。此实现减少了代码冗余,展示了模板与仿函数的强大灵活性。
181 2
|
4月前
|
存储 安全 Java
Java 集合面试题从数据结构到 HashMap 源码剖析详解及长尾考点梳理
本文深入解析Java集合框架,涵盖基础概念、常见集合类型及HashMap的底层数据结构与源码实现。从Collection、Map到Iterator接口,逐一剖析其特性与应用场景。重点解读HashMap在JDK1.7与1.8中的数据结构演变,包括数组+链表+红黑树优化,以及put方法和扩容机制的实现细节。结合订单管理与用户权限管理等实际案例,展示集合框架的应用价值,助你全面掌握相关知识,轻松应对面试与开发需求。
200 3
|
4月前
|
存储 C++ 容器
unordered_set、unordered_multiset、unordered_map、unordered_multimap的介绍及使用
unordered_set是不按特定顺序存储键值的关联式容器,其允许通过键值快速的索引到对应的元素。在unordered_set中,元素的值同时也是唯一地标识它的key。在内部,unordered_set中的元素没有按照任何特定的顺序排序,为了能在常数范围内找到指定的key,unordered_set将相同哈希值的键值放在相同的桶中。unordered_set容器通过key访问单个元素要比set快,但它通常在遍历元素子集的范围迭代方面效率较低。它的迭代器至少是前向迭代器。前向迭代器的特性。
186 0
|
4月前
|
编译器 C++ 容器
用一棵红黑树同时封装出map和set
再完成上面的代码后,我们的底层代码已经完成了,这时候已经是一个底层STL的红黑树了,已经已符合库里面的要求了,这时候我们是需要给他穿上对应的“衣服”,比如穿上set的“衣服”,那么这个穿上set的“衣服”,那么他就符合库里面set的要求了,同样map一样,这时候我们就需要实现set与map了。因此,上层容器map需要向底层红黑树提供一个仿函数,用于获取T当中的键值Key,这样一来,当底层红黑树当中需要比较两个结点的键值时,就可以通过这个仿函数来获取T当中的键值了。我们就可以使用仿函数了。
48 0
|
4月前
|
存储 编译器 容器
set、map、multiset、multimap的介绍及使用以及区别,注意事项
set是按照一定次序存储元素的容器,使用set的迭代器遍历set中的元素,可以得到有序序列。set当中存储元素的value都是唯一的,不可以重复,因此可以使用set进行去重。set默认是升序的,但是其内部默认不是按照大于比较,而是按照小于比较。set中的元素不能被修改,因为set在底层是用二叉搜索树来实现的,若是对二叉搜索树当中某个结点的值进行了修改,那么这棵树将不再是二叉搜索树。
202 0
|
7月前
|
存储 自然语言处理 数据库
【数据结构进阶】AVL树深度剖析 + 实现(附源码)
在深入探讨了AVL树的原理和实现后,我们不难发现,这种数据结构不仅优雅地解决了传统二叉搜索树可能面临的性能退化问题,还通过其独特的平衡机制,确保了在任何情况下都能提供稳定且高效的查找、插入和删除操作。
520 19
|
7月前
|
数据库 C++
【数据结构进阶】红黑树超详解 + 实现(附源码)
本文深入探讨了红黑树的实现原理与特性。红黑树是一种自平衡二叉搜索树,通过节点着色(红/黑)和特定规则,确保树的高度接近平衡,从而实现高效的插入、删除和查找操作。相比AVL树,红黑树允许一定程度的不平衡,减少了旋转调整次数,提升了动态操作性能。文章详细解析了红黑树的性质、插入时的平衡调整(变色与旋转)、查找逻辑以及合法性检查,并提供了完整的C++代码实现。红黑树在操作系统和数据库中广泛应用,其设计兼顾效率与复杂性的平衡。
1101 3