【Java超高频面试题&数据结构】数据结构相关高频面试题汇总

简介:

本期文章将汇总java数据结构相关的高频面试题,给最近需要找工作的朋友们总结一波,帮助大家全面掌握数据结构的核心知识点,提升竞争力,为你的面试之旅保驾护航!


说一下树的分类和理解

树是⼀种数据结构,它是由n(n>=1)个有限结点组成⼀个具有层次关系的集合。

        1. ⼆叉树

树中包含的各个节点的度不能超过 2 ,也就是任意节点最多只有两个分叉的树

①. ⼆叉排序树(⼆叉查找树)

⼆叉查找树是⼆叉树的衍⽣概念,也称为⼆叉搜索树

②. 平衡⼆叉树(也叫AVL树)

当且仅当任何节点的两棵⼦树的⾼度差不⼤于1的⼆叉树就是平衡树

③. 红⿊树(也叫RB树)

红⿊树也是⼀种⾃平衡⼆叉树,在平衡⼆叉树的基础上每个节点⼜增加了⼀个颜⾊的属性,节 点的颜⾊只能是红⾊或⿊⾊

        2. 多叉树

①. B - Tree (读音是B树,并非b减树)

是⼀种⾃平衡的树,能够保持数据有序。这种数据结构能够让查找数据、顺序访问、插⼊数据及删除的动作,都在对数时间内完成。

②. B + Tree

B+树就是基于B树的变种,多⽤数据库和⽂件系统,B+树上的叶⼦结点存储关键字以及相应记录的地址,叶⼦结点以上各层作为索引使⽤, Mysql的索引就是基于B+树实现的

b-tree和b+tree的区别

B+树就是基于B树的变种

B+树相对于B树的优势

每个节点存储更多的KEY,树的⾼度更低,时间复杂度越⼩,查询越快

数据在叶⼦节点,每次查询都要查询到叶⼦节点,查询速度⽐较稳定

叶⼦节点构成构成了链表结构,⽅便区间查询和排序

说一下ES用到了什么数据结构

ES是使用了数据索引存储结构,它是通过为关键字建立索引,通过索引找到对应的数据,这种索引也叫倒排索引,可以实现快速检索

Mysql为什么使用B+树:

以最小的IO找到更多的记录数,如果是B树,由于每个节点要存储Key和Value(数据),那么每个节点能存储的Key是很少的,

而B+树每个节点只存储Key,它可以存储更多的Key, 每个节点 存储的Key越多,路数越多,节点就越少,需要耗时的IO就越少,查找性能就越⾼,

且B+树 的叶⼦节点是有序的,形成链表,⽅便区间查询和排序

List和Set和Map的区别

List:

一个有序容器(元素存入集合的顺序和取出的顺序一致),元素可以重复,可以插入多个null元素,元素都有索引。常用的实现类有 ArrayList、LinkedList 和 Vector。

Set:

一个无序(存入和取出顺序有可能不一致)容器,不可以存储重复元素,只允许存入一个null元素,必须保证元素唯一性。Set 接口常用实现类是 HashSet、LinkedHashSet 以及 TreeSet。

Map:

是一个键值对集合,存储键、值和之间的映射。 Key无序,唯一;value 不要求有序,允许重复。Map没有继承于Collection接口,从Map集合中检索元素时,只要给出键对象,就会返回对应的值对象。

Map 的常用实现类:HashMap、TreeMap、HashTable、LinkedHashMap、ConcurrentHashMap。

集合与数组区别

1、数组声明了它容纳的元素的类型,而集合不声明。

2、数组是静态的,一个数组实例具有固定的大小,一旦创建了就无法改变容量了。而集合是可以动态扩展容量,可以根据需要动态改变大小,集合提供更多的成员方法,能满足更多的需求。

3、数组的存放的类型只能是一种(基本类型/引用类型),集合存放的类型可以不是一种(不加泛型时添加的类型是Object)。

4、数组是java语言中内置的数据类型,是线性排列的,执行效率或者类型检查都是最快的。

说一下Java中的集合体系

Collection接口

List: Collection的子接口, 元素可以重复, 而且是有序的

ArrayList:底层数据结构是数组,查询性能高,增删性能低

LinkedList:底层数据结构是双向链表,查询性能低,增删性能高

Vector:底层数据结构是数组,查询性能高,增删性能低

Set:Collection的子接口, 元素不可重复, 而且无序

HashSet:无序不重复的,使用HashMap的key存储元素,判断重复依据是hashCode()和equals()

linkedHashSet: 有序不重复,底层是哈希表+链表

TreeSet:有序不重复的,底层使用TreeMap的key存储元素,排序方式分为自然排序,比较器排序

Map接口: Map是存放键值对(Entry: key value) 的集合

HashMap:key的值没有顺序,线程不安全

TreeMap:key的值可以自然排序,线程不安全

HashTable:它的key和value都不允许为null,线程安全

Properties:它的key和value都是String类型的,线程安全

HashMap和HashTable的区别

HashMap:key的值没有顺序,线程不安全, 允许空值空键, 初始值容量16, 扩容2倍

HashTable:它的key和value都不允许为null,线程安全, 初始值容量11, 扩容为2n+1

HashMap性能高于HashTable

ArrayList和LinkedList区别

ArrayList底层数据结构是数组,查询性能高,增删性能低

LinkedList底层数据结构是双向链表,查询性能低,增删性能高

ArrayList和Vector区别

Vector是ArrayList的线程安全,

所以ArrayList的性能比Vector高

ArrayList的自动扩容是1.5倍, Vector是2倍

一个User的List集合,如何实现根据年龄排序?

使用Collections.sort()方法,传入user对象和重写Comparator定制比较器

Comparator方法返回使用包装类的compare()方法的结果即可

// 使用Collections.sort()方法和自定义的Comparator来对集合进行排序  
Collections.sort(users, new Comparator<User>() {  
    @Override  
    public int compare(User u1, User u2) {  
        return Integer.compare(u1.getAge(), u2.getAge());  
    }  
});  

哪些集合类是线程安全的

Vector

HashTable

ConcurrentHashMap

HashMap底层用到了那些数据结构?

JDK1.7及其之前:数组,链表 ;

JDK1.8开始:数组,链表,红黑树;

什么是Hash冲突

哈希冲突,也叫哈希碰撞,指的是两个不同的值,计算出了相同的hash,也就是两个不同的数据计算出同一个下标,通常解决方案有:

拉链法,把哈希碰撞的元素指向一个链表

开放寻址法,把产生冲突的哈希值作为值,再进行哈希运算,直到不冲突

再哈希法(再散列法),就是换一种哈希算法重来一次

建立公共溢出区,把哈希表分为基本表和溢出表,将产生哈希冲突的元素移到溢出表

HashMap为什么要用到链表结构

当我们向HashMap中添加元素时,会先根据key进行哈希运算,把hash值模与数组长度得到一个下标,然后将该元素添加进去。

但是如果产生了哈希碰撞,也就是不同的key计算出了相同的hash值,这就出问题了,因此它采用了拉链法来解决这个问题,将产生hash碰撞的元素,挂载到链表中

HashMap为什么要用到红黑树

当HashMap中同一个索引位置出现哈希碰撞的元素多了,链表会变得越来越长,查询效率会变得越来越慢。

因此在JDK1.8之后,当链表长度超过8个,会将链表转为红黑树来提高查询

HashMap链表和红黑树在什么情况下转换的?

当链表的长度大于等于8,同时数组的长度大于64,链表会自动转化为红黑树

扩容后当树中的节点数小于等于6,红黑树会自动转化为链表

HashMap在什么情况下扩容?HashMap如何扩容的?

HashMap的数组初始容量是16,负载因子是0.75,大于0.75会成倍扩容

成倍扩容:需要保证数组的长度是2的整数次幂

数组的长度必须是2的整数次幂:由于计算机的运算效率,加减法>乘法>除法>取模,取模的效率是最低的。将取模运算转化成了与运算,即数组长度减1的值和hash值的与运算,以此来优化性能。

HashMap是如何Put一个元素的

首先,将key进行hash运算,将这个hash值与上当前数组长度减1的值,计算出索引。

此时判断该索引位置是否已经有元素了,如果没有,就直接放到这个位置。

如果这个位置已经有元素了,也就是产生了哈希碰撞,那么判断旧元素的key和新元素的key的hash值是否相同,并且将他们进行equals比较,如果相同证明是同一个key,就覆盖旧数据,并将旧数据返回。

如果不相同的话,再判断当前桶是链表还是红黑树,如果是红黑树,就按红黑树的方式,写入该数据。

如果是链表,就依次遍历并比较当前节点的key和新元素的key是否相同,如果相同就覆盖,如果不同就接着往下找,直到找到空节点并把数据封装成新节点挂到链表尾部。

然后需要判断,当前链表的长度是否大于转化红黑树的阈值,如果大于就转化红黑树,最后判断数组长度是否需要扩容。

HashMap是如何Get一个元素的

首先将key进行哈希运算,计算出数组中的索引位置,判断该索引位置是否有元素,如果没有,就返回null,如果有值,判断该数据的key是否为查询的key,如果是就返回当前值的value

如果第一个元素的key不匹配,判断是红黑树还是链表,如果是红黑树,就就按照红黑树的查询方式查找元素并返回,如果是链表,就遍历并匹配key,让后返回value值

你知道HahsMap死循环问题吗

HashMap在扩容数组的时候,会将旧数据迁徙到新数组中,这个操作会将原来链表中的数据颠倒,比如a->b->null,转换成b->a->null

这个过程单线程是没有问题的,但是在多线程环境,就可能会出现a->b->a->b....,这就是死循环

在JDK1.8后,做了改进保证了转换后链表顺序一致,死循环问题得到了解决。但还是会出现高并发时数据丢失的问题,因此在多线程情况下还是建议使用ConcurrentHashMap来保证线程安全问题

说一下你对ConcurrentHashMap的理解

ConcurrentHashMap,它是HashMap的线程安全,支持高并发的版本

jdk1.7中,它是通过分段锁的方式来实现线程安全的。意思是将哈希表分成许多片段Segment,而Segment本质是一个可重入的互斥锁,所以叫做分段锁。

jdk1.8中,它是采用了CAS操作和synchronized来实现的,而且每个Node节点的value和next都用了volatile关键字修饰,保证了可见性


结语

🔥如果文章对你有帮助的话,欢迎💗关注、👍点赞、⭐收藏、✍️评论,支持一下小老弟,蟹蟹大咖们~ 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值