java 集合常见面试(一)

news2024/12/23 6:06:35

集合概述

java集合预览

Java 集合, 也叫作容器,主要是由两大接口派生而来:一个是 Collection接口,主要用于存放单一元素;另一个是 Map 接口,主要用于存放键值对。对于Collection 接口,下面又有三个主要的子接口:ListSetQueue

Java 集合框架如下图所示:

在这里插入图片描述

说说 List, Set, Queue, Map 四者的区别?

  • List(对付顺序的好帮手): 存储的元素是有序的、可重复的。
  • Set(注重独一无二的性质): 存储的元素是无序的、不可重复的。
  • Queue(实现排队功能的叫号机): 按特定的排队规则来确定先后顺序,存储的元素是有序的、可重复的。
  • Queue(实现排队功能的叫号机): 按特定的排队规则来确定先后顺序,存储的元素是有序的、可重复的。

集合框架底层数据结构总结

List

  • ArrayList: Object[] 数组
  • Vector:Object[] 数组
  • LinkedList: 双向链表(JDK1.6 之前为循环链表,JDK1.7 取消了循环)

Set

  • HashSet(无序,唯一): 基于 HashMap 实现的,底层采用 HashMap 来保存元素
  • LinkedHashSet: LinkedHashSetHashSet 的子类,并且其内部是通过LinkedHashMap 来实现的。有点类似于我们之前说的 LinkedHashMap 其内部是基于 HashMap 实现一样,不过还是有一点点区别的
  • TreeSet(有序,唯一): 红黑树(自平衡的排序二叉树)

Queue

  • PriorityQueue: Object[] 数组来实现二叉堆
  • ArrayQueue: Object[] 数组 + 双指针

再来看看 Map 接口下面的集合。

Map

  • HashMap: JDK1.8 之前 HashMap 由数组+链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的(“拉链法”解决冲突)。JDK1.8 以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间;
  • LinkedHashMap: LinkedHashMap 继承自 HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑;
  • Hashtable: 数组+链表组成的,数组是 Hashtable 的主体,链表则是主要为了解决哈希冲突而存在的;
  • TreeMap: 红黑树(自平衡的排序二叉树)

如何选用集合?

主要根据集合的特点来选用,比如我们需要根据键值获取到元素值时就选用 Map 接口下的集合,需要排序时选择 TreeMap,不需要排序时就选择 HashMap,需要保证线程安全就选用 ConcurrentHashMap

当我们只需要存放元素值时,就选择实现Collection 接口的集合,需要保证元素唯一时选择实现 Set 接口的集合比如 TreeSetHashSet,不需要就选择实现 List 接口的比如 ArrayListLinkedList,然后再根据实现这些接口的集合的特点来选用。

为什么要使用集合?

当我们需要保存一组类型相同的数据的时候,我们应该是用一个容器来保存,这个容器就是数组,但是,使用数组存储对象具有一定的弊端, 因为我们在实际开发中,存储的数据的类型是多种多样的,于是,就出现了“集合”,集合同样也是用来存储多个数据的。

数组的缺点是一旦声明之后,长度就不可变了;同时,声明数组时的数据类型也决定了该数组存储的数据的类型;而且,数组存储的数据是有序的、可重复的,特点单一。 但是集合提高了数据存储的灵活性,Java 集合不仅可以用来存储不同类型不同数量的对象,还可以保存具有映射关系的数据。

Collection 子接口之 List

ArrayList 和 Vector 的区别?

  • ArrayList 是 List 的主要实现类,底层使用 Object[]存储,适用于频繁的查找工作,线程不安全 ;
  • Vector 是 List 的古老实现类,底层使用Object[] 存储,线程安全的。

ArrayList 与 LinkedList 区别?

  • 是否保证线程安全: ArrayListLinkedList 都是不同步的,也就是不保证线程安全;
  • 底层数据结构: ArrayList 底层使用的是 Object 数组;LinkedList 底层使用的是 双向链表 数据结构(JDK1.6 之前为循环链表,JDK1.7 取消了循环。注意双向链表和双向循环链表的区别,下面有介绍到!)
  • 插入和删除是否受元素位置的影响:
    • ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。 比如:执行add(E e)方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是 O(1)。但是如果要在指定位置 i 插入和删除元素的话(add(int index, E element))时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。
    • LinkedList 采用链表存储,所以,如果是在头尾插入或者删除元素不受元素位置的影响(add(E e)、addFirst(E e)、addLast(E e)、removeFirst() 、 removeLast()),时间复杂度为 O(1),如果是要在指定位置 i 插入和删除元素的话(add(int index, E element),remove(Object o)), 时间复杂度为 O(n) ,因为需要先移动到指定位置再插入。
  • 是否支持快速随机访问: LinkedList 不支持高效的随机元素访问,而 ArrayList(实现了RandomAccess接口) 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index)方法)。
  • 内存空间占用: ArrayList 的空间浪费主要体现在在 list 列表的结尾会预留一定的容量空间,而 LinkedList 的空间花费则体现在它的每一个元素都需要消耗比 ArrayList 更多的空间(因为要存放直接后继和直接前驱以及数据)

补充内容:双向链表和双向循环链表

双向链表: 包含两个指针,一个 prev 指向前一个节点,一个 next 指向后一个节点。
在这里插入图片描述

双向循环链表: 最后一个节点的 next 指向 head,而 head 的 prev 指向最后一个节点,构成一个环。
在这里插入图片描述

补充内容:RandomAccess 接口

public interface RandomAccess {
}

查看源码我们发现实际上 RandomAccess 接口中什么都没有定义。所以,在我看来 RandomAccess 接口不过是一个标识罢了。标识什么? 标识实现这个接口的类具有随机访问功能。

binarySearch() 方法中,它要判断传入的 list 是否 RandomAccess 的实例,如果是,调用indexedBinarySearch()方法,如果不是,那么调用iteratorBinarySearch()方法

 public static <T>
 int binarySearch(List<? extends Comparable<? super T>> list, T key) {
 if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
     return Collections.indexedBinarySearch(list, key);
 else
     return Collections.iteratorBinarySearch(list, key);
 }

**ArrayList 实现了 RandomAccess 接口, 而 LinkedList 没有实现。为什么呢?**我觉得还是和底层数据结构有关!ArrayList 底层是数组,而 LinkedList 底层是链表。数组天然支持随机访问,时间复杂度为 O(1),所以称为快速随机访问。链表需要遍历到特定位置才能访问特定位置的元素,时间复杂度为 O(n),所以不支持快速随机访问。,ArrayList 实现了 RandomAccess 接口,就表明了他具有快速随机访问功能。 RandomAccess 接口只是标识,并不是说 ArrayList 实现 RandomAccess 接口才具有快速随机访问功能的!

说一说 ArrayList 的扩容机制吧

Collection 子接口之 Set

comparable 和 Comparator 的区别

  • comparable 接口实际上是出自java.lang包 它有一个 compareTo(Object obj)方法用来排序
  • comparator接口实际上是出自 java.util 包它有一个compare(Object obj1, Object obj2)方法用来排序

一般我们需要对一个集合使用自定义排序时,我们就要重写compareTo()方法或compare()方法,当我们需要对某一个集合实现两种排序方式,比如一个 song 对象中的歌名和歌手名分别采用一种排序方法的话,我们可以重写compareTo()方法和使用自制的Comparator方法或者以两个 Comparator 来实现歌名排序和歌星名排序,第二种代表我们只能使用两个参数版的 Collections.sort()

Comparator 定制排序

        ArrayList<Integer> arrayList = new ArrayList<Integer>();
        arrayList.add(-1);
        arrayList.add(3);
        arrayList.add(3);
        arrayList.add(-5);
        arrayList.add(7);
        arrayList.add(4);
        arrayList.add(-9);
        arrayList.add(-7);
        System.out.println("原始数组:");
        System.out.println(arrayList);
        // void reverse(List list):反转
        Collections.reverse(arrayList);
        System.out.println("Collections.reverse(arrayList):");
        System.out.println(arrayList);

        // void sort(List list),按自然排序的升序排序
        Collections.sort(arrayList);
        System.out.println("Collections.sort(arrayList):");
        System.out.println(arrayList);
        // 定制排序的用法
        Collections.sort(arrayList, new Comparator<Integer>() {

            @Override
            public int compare(Integer o1, Integer o2) {
                return o2.compareTo(o1);
            }
        });
        System.out.println("定制排序后:");
        System.out.println(arrayList);

Output:

原始数组:
[-1, 3, 3, -5, 7, 4, -9, -7]
Collections.reverse(arrayList):
[-7, -9, 4, 7, -5, 3, 3, -1]
Collections.sort(arrayList):
[-9, -7, -5, -1, 3, 3, 4, 7]
定制排序后:
[7, 4, 3, 3, -1, -5, -7, -9]

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

// person对象没有实现Comparable接口,所以必须实现,这样才不会出错,才可以使treemap中的数据按顺序排列
// 前面一个例子的String类已经默认实现了Comparable接口,详细可以查看String类的API文档,另外其他
// 像Integer类等都已经实现了Comparable接口,所以不需要另外实现了
public  class Person implements Comparable<Person> {
    private String name;
    private int age;

    public Person(String name, int age) {
        super();
        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;
    }

    /**
     * T重写compareTo方法实现按年龄来排序
     */
    @Override
    public int compareTo(Person o) {
        if (this.age > o.getAge()) {
            return 1;
        }
        if (this.age < o.getAge()) {
            return -1;
        }
        return 0;
    }
}
    public static void main(String[] args) {
        TreeMap<Person, String> pdata = new TreeMap<Person, String>();
        pdata.put(new Person("张三", 30), "zhangsan");
        pdata.put(new Person("李四", 20), "lisi");
        pdata.put(new Person("王五", 10), "wangwu");
        pdata.put(new Person("小红", 5), "xiaohong");
        // 得到key的值的同时得到key所对应的值
        Set<Person> keys = pdata.keySet();
        for (Person key : keys) {
            System.out.println(key.getAge() + "-" + key.getName());

        }
    }

Output:

5-小红
10-王五
20-李四
30-张三

无序性和不可重复性的含义是什么

  • 无序性不等于随机性 ,无序性是指存储的数据在底层数组中并非按照数组索引的顺序添加 ,而是根据数据的哈希值决定的。
  • 不可重复性是指添加的元素按照 equals() 判断时 ,返回 false,需要同时重写 equals() 方法和 hashCode() 方法。

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

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

Collection 子接口之 Queue

Queue 与 Deque 的区别

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

Queue 扩展了 Collection 的接口,根据 因为容量问题而导致操作失败后处理方式的不同 可以分为两类方法: 一种在操作失败后会抛出异常,另一种则会返回特殊值。
在这里插入图片描述

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

Deque 扩展了 Queue 的接口, 增加了在队首和队尾进行插入和删除的方法,同样根据失败后处理方式的不同分为两类:

在这里插入图片描述
事实上,Deque 还提供有 push() 和 pop() 等其他方法,可用于模拟栈。

ArrayDeque 与 LinkedList 的区别

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

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

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

PriorityQueue

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

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

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

PriorityQueue 在面试中可能更多的会出现在手撕算法的时候,典型例题包括堆排序、求第K大的数、带权图的遍历等,所以需要会熟练使用才行。

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

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

相关文章

预告|第四届OpenI/O启智开发者大会NLP大模型论坛强势来袭!

最近&#xff0c;ChatGPT刷爆了所有人的朋友圈。它不仅能够与人类进行日常自然的聊天&#xff0c;还能胜任如写论文、编代码等诸多较为复杂的语言工作。ChatGPT 爆火的背后&#xff0c;是NLP(自然语言处理)技术的飞速革新。在过去的十年里&#xff0c;人工神经网络计算的加入、…

#461 年轻人的世界没有容易二字,除了脱发

点击文末“阅读原文”即可收听本期节目剪辑、音频 / 卷圈 编辑 / SandLiu 卷圈 监制 / 姝琦 文案 / 粒粒 产品统筹 / bobo 录音间 / 声湃轩提起二月二&#xff0c;你一定会脱口而出“龙抬头”。龙抬头吃什么很重要&#xff0c;重要到可以吵一架&#xff0c;但比吃什么更重要…

echo和swagger的结合使用(oapi-codegen使用)

echo和swagger的结合使用&#xff08;oapi-codegen使用&#xff09; 相关官网&#xff1a; echo官网swagger 这里介绍的重点是swagger和echo的整合使用&#xff0c;具体的框架的使用方法请看官方文档。 1. 初衷 swagger官网提供了文档转代码的操作&#xff0c;但转出来的代…

Allegro如何通过报表的方式检查单板上是否有假器件操作指导

Allegro如何通过报表的方式检查单板上是否有假器件操作指导 在做PCB设计的时候,输出生产文件之前,必须保证PCB上不能存在假器件,如下图,是不被允许的 当PCB单板比较大,如何通过报表的方式检查是否存在假器件,具体操作如下 点击Tools点击Reports

你看,ChatGPT都知道优先使用BigDecimal

不是三婶儿偏执&#xff0c;非要吐槽。家人们&#xff0c;咱就是说&#xff0c;按照基操逻辑谁会把严格金额计算相关的数据使用double类型呢… “我以为吕布已经够勇猛了&#xff0c;这是谁的部下&#xff1f;” 前几天&#xff0c;一同事让帮忙写段代码。内容比较常规&#xf…

iTerm2 的配置与美化

前言 iTerm2是默认终端的替代品&#xff0c;也是目前Mac系统下最好用的终端工具&#xff0c;集颜值和效率于一身。 最近换了一台新的 MacBook Pro &#xff0c;需要重装电脑上的常用软件。为了将 ITerm2 DIY为自己喜欢的样子&#xff0c;花了不少时间。为了方便下次再配置&…

python五子棋代码最简单的,python五子棋代码画棋盘

大家好&#xff0c;本文将围绕python五子棋代码输赢逻辑判断展开说明&#xff0c;如何用python制作五子棋游戏是一个很多人都想弄明白的事情&#xff0c;想搞清楚python五子棋代码最简单的需要先了解以下几个事情。 1、求解用python 编写五子棋怎样编写判断输赢的函数&#xff…

C++项目——高并发内存池(1)--介绍及定长内存池

1.什么是内存池 1.1 池化技术 将程序中需要经常使用的核心资源先申请出来&#xff0c;放在一个池内&#xff0c;由程序自己管理&#xff0c;这样可以提高资源的使用效率&#xff0c;也可以保证本程序占有的资源数量。 比如之前博文实现的线程池&#xff0c;就是预先的申请出…

CRM哪家好?这5个CRM管理系统很好用!

CRM哪家好&#xff1f;这5个CRM管理系统很好用&#xff01; CRM(Customer Relationship Management)即客户关系管理&#xff0c;能够帮助提高客户的价值、满意度、赢利性和忠实度&#xff0c;缩减销售周期和销售成本、增加收入、寻找扩展业务所需的新的市场和渠道&#xff0c;…

浅谈c++引用

浅谈c 在这里开设 <<浅谈C>> 系列专题,针对C重点内容展开探讨与观察底层,同时也是一个面试专栏,所选知识大多为面试常见问题.前期较为基础,难度会逐渐上升哦~ 本专栏采用经典的哲学三段论编写:是什么|为什么|怎么做 力图精简,高效. 第一章: 浅谈C函数重载 传送门…

ansible剧本(playbook)

目录 一、playbooks 概述以及实例操作 1、playbooks 的组成 2、操作示例一&#xff1a; 2.1 编写yaml文件也就是playbook 2.2 修改配置文件并放入/opt/目录下 2.3 运行playbook 3、操作实例二&#xff1a;定义、引用变量 4、操作示例三&#xff1a;指定远程主机sudo切换…

音频格式、参数、开发相关总结

常见的语音格式介绍PCM&#xff1a;音频纯裸数据。WAV&#xff1a;微软在windows存储的一种纯裸数据格式。AIFF&#xff1a;苹果在Mac上存储的一种纯裸数据格式。MP3&#xff1a;为ISO/IEC国际标准&#xff0c;是现在最普及的一种数字音频编码和有损压缩格式&#xff0c;几乎所…

Android Spider XX兔 Wechat Cookie 校验 注册案例(二)

声明 此次案例只为学习交流使用&#xff0c;抓包内容、敏感网址、数据接口均已做脱敏处理&#xff0c;切勿用于其他非法用途&#xff1b; 文章目录声明前言一、资源推荐二、任务说明三、App抓包分析四、还原JS加密1、Date类2、MD5类3、组合调用还原 api_token 参数4、execjs模…

04- 图像卷积及图片的模糊和边缘检测 (OpenCV基础) (机器视觉)

知识重点 padding指的就是填充的0的圈数重构图片大小: img cv2.resize(img, dsize(300, 300))模糊操作: dst cv2.filter2D(img, -1, kernel) kernel np.ones((5, 5), np.float32)/ 25 dst cv2.filter2D(img, -1, kernel) # 卷积操作 模糊操作: 方盒滤波: dst cv2.b…

AD使用教程 图文并茂 AD2020四层板

文章目录设计流程图&#xff1a;资源下载&#xff1a;自定义快捷键&#xff1a;&#xff08;按照个人习惯&#xff09;默认快捷键&#xff1a;一、软件主页面1.主界面2.Panels面板3.系统设置3.自定义快捷键4.新建工程二、原理图库1.原理图库面板2.管脚属性三、原理图绘制绘制步…

C++011-C++循环+枚举

文章目录C011-C循环枚举枚举枚举思想枚举举例题目描述 统计因数题目描述 质数判定错误方法一&#xff1a;优化方法1&#xff1a; 用break实现优化优化方法2&#xff1a; sqrt(n)题目描述 水仙花数题目描述 7744问题实现方法1优化方法2题目描述 余数相同问题题目描述 特殊自然数…

《第一行代码》 第六章:数据库与存储方案

一&#xff0c;持久化技术简介 也就是数据存储在内存中&#xff0c;会丢失。需要存储在存储设备中。而持久化技术&#xff0c;就是提供了手段&#xff0c;让数据再试瞬时状态和持久状态之间转换。 安卓开发提供了三种数据的持久化技术&#xff1a; 二&#xff0c;文件存储 …

【HDFS】FsDatasetImpl#getReplicaVisibleLength

调用点 DataNode.getReplicaVisibleLength(ExtendedBlock) (org.apache.hadoop.hdfs.server.datanode) // ClientDataNodeProtocolBlockChecksumComputer in BlockChecksumHelper.BlockChecksumComputer(DataNode, ExtendedBlock, BlockChecksumOptions) (org.apache.hadoop.hd…

ros中时间的概念:ros::Time、ros::Duration、定时器ros::Timerros::Rate、ros::WallTime

1. ros时间格式说明 有时刻和持续时长(可以是负数)&#xff0c;分为秒和纳秒&#xff0c;换算关系&#xff1a;1sec1e9nsec。Time指的是某个时刻&#xff0c;而Duration指的是某个时段。 int32 sec int32 nsec 2. ros::Time::now() 记录当前时刻 3. ros::Duration 代表持续的…

Linux 输出重定向 “>”、“>>”、“freopen”

有时候&#xff0c;我们编码时会使用printf或fprintf去打印输出调试信息或者报错信息&#xff0c;但正常这样去打印&#xff0c;只会显示在终端&#xff0c;如果终端关闭了&#xff0c;或者系统宕机了等&#xff0c;这些输出信息就没有了&#xff0c;为了将这些重要的信息保存下…