【JavaGuide面试总结】Java集合篇·中

news2024/11/16 0:56:28

【JavaGuide面试总结】Java集合篇·中

  • 1.Collection 子接口之 Set
    • Comparable 和 Comparator 的区别
    • 比较 HashSet、LinkedHashSet 和 TreeSet 三者的异同
  • 2.Collection 子接口之 Queue
    • Queue 与 Deque 的区别
    • ArrayDeque 与 LinkedList 的区别
    • 说一说 PriorityQueue
  • 3.Map 接口
    • HashMap 的底层实现
      • JDK1.8 之前
      • JDK1.8 之后
    • HashMap 和 Hashtable 的区别
    • HashMap 和 HashSet 区别
    • HashMap 和 TreeMap 区别

1.Collection 子接口之 Set

Comparable 和 Comparator 的区别

字面含义不同:

Comparable 翻译为中文是“比较”的意思,而 Comparator 是“比较器”的意思。Comparable 是以 -able 结尾的,表示它自身具备着某种能力,而 Comparator 是以 -or 结尾,表示自身是比较的参与者

用法不同:

Comparable 接口只有一个方法 compareTo,实现 Comparable 接口并重写 compareTo 方法就可以实现某个类的排序了

案例:重写 compareTo 方法实现按年龄来排序

public class Person implements Comparable<Person>{
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    /**
     * 重写compareTo方法实现按年龄来排序
     * @param o the object to be compared.
     * @return 1 -1 or 0
     */
    @Override
    public int compareTo(Person o) {
        if (this.age > o.getAge()) {
            return 1;
        }
        if (this.age < o.getAge()) {
            return -1;
        }
        return 0;
    }
}

测试类:

public class Main {
    public static void main(String[] args) {
        TreeMap<Person, String> pdata = new TreeMap<>();
        pdata.put(new Person("张三",30),"zhangsan");
        pdata.put(new Person("里斯",12),"lisi");
        pdata.put(new Person("王五",11),"wangwu");
        pdata.put(new Person("赵六",66),"zhaoliu");
        Set<Person> keys = pdata.keySet();
        for (Person key : keys) {
            System.out.println(key.getAge() + "-" + key.getName());
            // 11-王五
            // 12-里斯
            // 30-张三
            // 66-赵六
        }
    }
}

Comparator 排序的方法是 compare:

案例:使用Comparator实现定制排序:(可以基于匿名内部类实现)

public class Main {
    public static void main(String[] args) {
        ArrayList<Integer> arrayList = new ArrayList<>();
        arrayList.add(-1);
        arrayList.add(521);
        arrayList.add(1314);
        arrayList.add(17);
        System.out.println(arrayList); // [-1, 521, 1314, 17]
        // 定制排序的用法
        Collections.sort(arrayList, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2.compareTo(o1);
            }
        });
        System.out.println(arrayList);  // [1314, 521, 17, -1]
    }
}

Comparable 可以看作是“对内”进行排序接口,而 Comparator 是“对外”进行排序的接口🐭

比较 HashSet、LinkedHashSet 和 TreeSet 三者的异同

  • HashSetLinkedHashSetTreeSet 都是 Set 接口的实现类,都能保证元素唯一,并且都不是线程安全的。
  • HashSetLinkedHashSetTreeSet 的主要区别在于底层数据结构不同。HashSet 的底层数据结构是哈希表(基于 HashMap 实现)。LinkedHashSet 的底层数据结构是链表和哈希表,元素的插入和取出顺序满足先入先出。TreeSet 底层数据结构是红黑树,元素是有序的,排序的方式有自然排序和定制排序。
  • 底层数据结构不同又导致这三者的应用场景不同。HashSet 用于不需要保证元素插入和取出顺序的场景,LinkedHashSet 用于保证元素的插入和取出顺序满足 FIFO 的场景,TreeSet 用于支持对元素自定义排序规则的场景。

2.Collection 子接口之 Queue

Queue 与 Deque 的区别

Queue 是单端队列,只能从一端插入元素,另一端删除元素,实现上一般遵循 先进先出(FIFO) 规则。

Deque 是双端队列,在队列的两端均可以插入或删除元素。

事实上,Deque 还提供有 push()pop() 等其他方法,可用于模拟栈。

ArrayDeque 与 LinkedList 的区别

ArrayDequeLinkedList 都实现了 Deque 接口,两者都具有队列的功能,但两者有什么区别呢?

  • ArrayDeque 是基于可变长的数组和双指针来实现,而 LinkedList 则通过链表来实现。
  • ArrayDeque 不支持存储 NULL 数据,但 LinkedList 支持。
  • ArrayDeque 插入时可能存在扩容过程, 不过均摊后的插入操作依然为 O(1)。虽然 LinkedList 不需要扩容,但是每次插入数据时均需要申请新的堆空间,均摊性能相比更慢。

从性能的角度上,选用 ArrayDeque 来实现队列要比 LinkedList 更好。此外,ArrayDeque 也可以用于实现栈🐻‍❄️

说一说 PriorityQueue

PriorityQueue 是在 JDK1.5 中被引入的, 其与 Queue 的区别在于元素出队顺序是与优先级相关的,即总是优先级最高的元素先出队。

这里列举其相关的一些要点:

  • PriorityQueue 利用了二叉堆的数据结构来实现的,底层使用可变长的数组来存储数据
  • PriorityQueue 通过堆元素的上浮和下沉,实现了在 O(logn) 的时间复杂度内插入元素和删除堆顶元素。
  • PriorityQueue 是非线程安全的,且不支持存储 NULLnon-comparable 的对象。
  • PriorityQueue 默认是小顶堆,但可以接收一个 Comparator 作为构造参数,从而来自定义元素优先级的先后。

3.Map 接口

HashMap 的底层实现

JDK1.8 之前

JDK1.8 之前 HashMap 底层是 数组和链表 结合在一起使用也就是 链表散列

HashMap 通过 key 的 hashcode 经过扰动函数处理过后得到 hash 值,然后通过 (n - 1) & hash 判断当前元素存放的位置(这里的 n 指的是数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的 hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。

所谓扰动函数指的就是 HashMap 的 hash 方法。使用 hash 方法也就是扰动函数是为了防止一些实现比较差的 hashCode() 方法 换句话说使用扰动函数之后可以减少碰撞。

JDK 1.8 HashMap 的 hash 方法源码:

static final int hash(Object key) {
	int h;
	// key.hashCode():返回散列值也就是hashcode
	// ^ :按位异或
	// >>>:无符号右移,忽略符号位,空位都以0补齐
	return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

所谓 “拉链法” 就是:将链表和数组相结合。也就是说创建一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中即可。

jdk1.8 之前的内部结构-HashMap

JDK1.8 之后

相比于之前的版本, JDK1.8 之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。

jdk1.8之后的内部结构-HashMap

TreeMap、TreeSet 以及 JDK1.8 之后的 HashMap 底层都用到了红黑树。红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构🐸

我们来结合源码分析一下 HashMap 链表到红黑树的转换:

1、 putVal 方法中执行链表转红黑树的判断逻辑

链表的长度大于 8 的时候,就执行 treeifyBin (转换红黑树)的逻辑。

将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树🦓

// 遍历链表
for (int binCount = 0; ; ++binCount) {
    // 遍历到链表最后一个节点
    if ((e = p.next) == null) {
        p.next = newNode(hash, key, value, null);
        // 如果链表元素个数大于等于TREEIFY_THRESHOLD(8)
        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
            // 红黑树转换(并不会直接转换成红黑树)
            treeifyBin(tab, hash);
        break;
    }
    if (e.hash == hash &&
        ((k = e.key) == key || (key != null && key.equals(k))))
        break;
    p = e;
}

2、treeifyBin 方法中判断是否真的转换为红黑树

final void treeifyBin(Node<K,V>[] tab, int hash) {
    int n, index; Node<K,V> e;
    // 判断当前数组的长度是否小于 64
    if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
        // 如果当前数组的长度小于 64,那么会选择先进行数组扩容
        resize();
    else if ((e = tab[index = (n - 1) & hash]) != null) {
        // 否则才将列表转换为红黑树

        TreeNode<K,V> hd = null, tl = null;
        do {
            TreeNode<K,V> p = replacementTreeNode(e, null);
            if (tl == null)
                hd = p;
            else {
                p.prev = tl;
                tl.next = p;
            }
            tl = p;
        } while ((e = e.next) != null);
        if ((tab[index] = hd) != null)
            hd.treeify(tab);
    }
}

HashMap 和 Hashtable 的区别

  • 线程是否安全: HashMap 是非线程安全的,Hashtable 是线程安全的,因为 Hashtable 内部的方法基本都经过synchronized 修饰。
  • 效率: 因为线程安全的问题,HashMap 要比 Hashtable 效率高一点。另外,Hashtable 基本被淘汰,不要在代码中使用它;
  • 对 Null key 和 Null value 的支持: HashMap 可以存储 null 的 key 和 value,但 null 作为键只能有一个,null 作为值可以有多个;Hashtable 不允许有 null 键和 null 值,否则会抛出 NullPointerException
  • 底层数据结构: JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)时,将链表转化为红黑树(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树),以减少搜索时间(后文中我会结合源码对这一过程进行分析)。Hashtable 没有这样的机制。
  • 初始容量大小和每次扩充容量大小的不同 : ① 创建时如果不指定容量初始值,Hashtable 默认的初始大小为 11,之后每次扩充,容量变为原来的 2n+1。HashMap 默认的初始化大小为 16。之后每次扩充,容量变为原来的 2 倍。② 创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为 2 的幂次方大小。也就是说 HashMap 总是使用 2 的幂作为哈希表的大小

HashMap 和 HashSet 区别

如果你看过 HashSet 源码的话就应该知道:HashSet 底层就是基于 HashMap 实现的。

HashMapHashSet
实现了 Map 接口实现 Set 接口
存储键值对仅存储对象
调用 put()向 map 中添加元素调用 add()方法向 Set 中添加元素
HashMap 使用键(Key)计算 hashcodeHashSet 使用成员对象来计算 hashcode 值,对于两个对象来说 hashcode 可能相同,所以equals()方法用来判断对象的相等性

HashMap 和 TreeMap 区别

TreeMapHashMap 都继承自AbstractMap ,但是需要注意的是TreeMap它还实现了NavigableMap接口和SortedMap 接口。

实现 NavigableMap 接口让 TreeMap 有了对集合内元素的搜索的能力。

实现SortedMap接口让 TreeMap 有了对集合中的元素根据键排序的能力。默认是按 key 的升序排序。

综上,相比于HashMap来说 TreeMap 主要多了对集合中的元素根据键排序的能力以及对集合内元素的搜索的能力🐔

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/181473.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器学习(八):深度学习简介

文章目录 深度学习简介 一、神经网络简介 二、深度学习各层负责内容 深度学习简介 一、神经网络简介 深度学习&#xff08;Deep Learning&#xff09;&#xff08;也称为深度结构学习【Deep Structured Learning】、层次学习【Hierarchical Learning】或者是深度机器学习【…

React中commit阶段发生了什么

对于commit阶段的主要工作是循环effectList链表去将有更新的fiber节点应用到页面上是commit的主要工作。 EffectList 什么是副作用&#xff1f; 函数在执行过程中对外部造成的影响可以称之为副作用&#xff0c;副作用包含的类型很多&#xff0c;比如说标记值为Placement时&a…

客快物流大数据项目(一百零九):Spring Boot概述

文章目录 Spring Boot概述 一、什么是SpringBoot 二、​​​​​​​为什么要学习Spring Boot

PHP转Go实践:xjson解析神器「开源工具集」

前言 近期会更新一系列开源项目的文章&#xff0c;新的一年会和大家做更多的开源项目&#xff0c;也欢迎大家加入进来。 xutil 今天分享的文章源自于开源项目jinzaigo/xutil的封装。 在封装过程中&#xff0c;劲仔将实现原理以及相关实践思考&#xff0c;写成文章分享出来&am…

Python3学习——条件控制、循环语句与迭代器

目录 一、编程第一步——斐波那契数列 二、条件控制 (一)if/else语句 判断狗狗的年龄&#xff1a; (二)多层if/else嵌套 判断数字能否被2或3整除&#xff1a; (三)match...case匹配——python3中新增 根据数字判断星期&#xff1a; 三、循环语句 (一)while循环 1.循环…

Java:Idea创建项目和Spring工程基本使用

一、创建项目 1、创建新的空的项目&#xff1a; Empty Project–next 2、定义项目的名称&#xff0c;并指定位置 3、对项目进行设置&#xff0c;JDK版本、编译版本 4、添加模块信息 5、修改maven路径 6、项目目录结构 二、搭建Spring的框架 1、在核心配置文件中添加Spring的j…

C++11 并发指南五(stdcondition_variable 详解)

C11 并发指南五(std::condition_variable 详解) 文章目录C11 并发指南五(std::condition_variable 详解)std::condition_variable 类介绍std::condition_variable_any 介绍std::cv_status 枚举类型介绍std::notify_all_at_thread_exit前面三讲《 C11 并发指南二(std::thread 详…

二叉树简单解析(1)

&#x1f340;本人简介&#xff1a; 吉师大一最爱逃课的混子、 华为云享专家、阿里云专家博主、腾讯云自媒体分享计划博主、 华为MindSpore优秀开发者、迷雾安全团队核心成员&#xff0c;CSDN2022年运维与安全领域第15名 &#x1f341;本人制作小程序以及资源分享地址&#x…

英语学习打卡day7

2023.1.27 1.ironically adv.具有讽刺意味的是;反讽地&#xff0c;讽刺地 Ironically, his cold got better on the last day of his holiday. 2.bequeath vt.遗赠;把…遗赠给;把… .传给 (比give更正式) bequeath sb sth bequeath sth to sb Don’t bequeath the problem …

JDK17 || JDK 8 完美 卸载 教程 (Windows版)

文章目录一、卸载jdk程序1 . 找到控制面板2. 卸载程序3. 找到JDK 相关的程序4. 右键 选择卸载程序5. 下一步 选择 是6.下一步 选择 是二、安装 新版 JDK三、如果不想再使用jdk环境结语一、卸载jdk程序 1 . 找到控制面板 2. 卸载程序 3. 找到JDK 相关的程序 4. 右键 选择卸载程…

IDEA界面和控制台的滚动条颜色不明显?赶快换一个吧!

前言 不知道大家是否和我一样有这么一个烦恼&#xff1a; IDEA自带的滚动条颜色很暗&#xff0c;配上一些主题颜色搭配很难发现。 所以今天就想着怎么可以修改滚动条颜色&#xff0c;首先去网上搜了搜都是什么鼠标滚轮加shift滚动&#xff0c;一点也不实用 偶然看到了个不错的…

【青训营】Go的BenchMark的使用

本文内容总结于 字节跳动青年训练营 第五届后端组 Go自带了一些性能测试工具&#xff0c;其中BenchMark是较为重要的一个。 我们以计算斐波那契数列的示例来展示BenchMark的使用 package Benchmarkimport "testing"func Fib(n int) int {if n < 2 {return n}ret…

OpenCV-PyQT项目实战(1)安装与环境配置

本系列从零开始实战解说基于 PyQt5 的 OpenCV 项目开发。 欢迎关注『OpenCV-PyQT项目实战 Youcans』系列&#xff0c;持续更新中 OpenCV-PyQT项目实战&#xff08;1&#xff09;安装与环境配置 OpenCV-PyQT项目实战&#xff08;2&#xff09;OpenCV导入图像 文章目录1. PyQt5 …

初识图像分类——K近邻法(cs231n assignment)

作者&#xff1a;非妃是公主 专栏&#xff1a;《计算机视觉》 个性签&#xff1a;顺境不惰&#xff0c;逆境不馁&#xff0c;以心制境&#xff0c;万事可成。——曾国藩 专栏系列文章 Cannot find reference ‘imread‘ in ‘init.py‘ error: (-209:Sizes of input arguments…

ppt神器islide 第1节 初步接触强大的工具-资源篇

ppt神器islide 第1节 初步接触强大的工具1 PPT大神的课程总结1.1 骨架篇1.2 色彩篇1.3 对齐篇1.4 对比篇1.5 修饰篇1.6 字体篇1.7 素材篇1.8 线条篇1.8.1 可以随意画线条&#xff0c;填充空白1.8.2 在字体上画线条&#xff0c;做成艺术字1.8.3 做对称线条&#xff0c;比如递进三…

[Vulnhub] DC-3

下载链接&#xff1a;DC-3 DC-3需要 把IDE里面的改成IDE 0:0 不然无法打开 知识点&#xff1a; Joomla cms 3.7.0 sql注入漏洞cmseek工具探测cms指纹john解密proc_popen反弹shellpython -m http.server开启http服务 & wget远程下载ubuntu16.0.4 Linux 4.4.0-21 系统漏…

使用OpenAI的Whisper 模型进行语音识别

语音识别是人工智能中的一个领域&#xff0c;它允许计算机理解人类语音并将其转换为文本。该技术用于 Alexa 和各种聊天机器人应用程序等设备。而我们最常见的就是语音转录&#xff0c;语音转录可以语音转换为文字记录或字幕。 wav2vec2、Conformer 和 Hubert 等最先进模型的最…

无穷小的比较——“高等数学”

各位CSDN的uu们你们好呀&#xff0c;今天小雅兰的内容是无穷小的比较&#xff0c;下面&#xff0c;就让我们一起进入高等数学的世界吧 回顾 定义 性质 定理 定理1&#xff1a; 定理2&#xff1a;等价无穷小替换定理 常用的等价无穷小 例题 小结 回顾 两个无穷小的商当然不一定还…

Android深入系统完全讲解(38)

9.6 配置 native 方法 9.6.1 CMakeLists.txt 文件中内容&#xff0c;配置生成一个 so 库add_library( # Sets the name of the library. native-lib # Sets the library as a shared library. SHARED # Provides a relative path to your source file(s). src/main/cpp/native…

一刷代码随想录——数组

概念数组是存放在连续内存空间上的相同类型数据的集合连续&#xff1a;增删需移动后续元素&#xff0c;删除的本质是覆盖类型&#xff1a;相同用数组&#xff0c;不同用结构数组下标从0开始C中二维数组连续分布vector的底层实现是array&#xff0c;严格来讲vector是容器&#x…