高级java面试题-详细版

1、String类为什么是final类型?

(1)为了实现字符串池

(2)为了线程安全

(3)为了实现String可以创建HashCode不可变性

final可以修饰类、变量和方法,并且被修饰的类不能被继承,即它不能拥有自己的子类,被final修饰的方法不能被重写,final修饰的变量,无论是类属性,对象属性、形参还是局部变量,都需要进行初始化操作。

那么为什么保证String不可变呢?因为只有当字符串是不可变的,字符串池才有可能实现。字符串池的实现可以在运行时节约很多heap空间,因为不同的字符串变量都指向池中的同一个字符串。但如果字符串是可变的,那么String interning将不能实现,因为这样的话,如果变量改变了它的值,那么其它指向这个值的变量的值也会一起改变。

如果字符串是可变的,那么会引起很严重的安全问题。譬如,数据库的用户名、密码都是以字符串的形式传入来获得数据库的连接,或者在socket编程中,主机名和端口都是以字符串的形式传入。因为字符串是不可变的,所以它的值是不可改变的,否则黑客们可以钻到空子,改变字符串指向的对象的值,造成安全漏洞。

因为字符串是不可变的,所以是多线程安全的,同一个字符串实例可以被多个线程共享。这样便不用因为线程安全问题而使用同步。字符串自己便是线程安全的。

因为字符串是不可变的,所以在它创建的时候HashCode就被缓存了,不需要重新计算。这就使得字符串很适合作为Map中的键,字符串的处理速度要快过其它的键对象。这就是HashMap中的键往往都使用字符串。

2、String、Stringbuffer和StringBuilder的区别

地址来源:https://blog.csdn.net/csxypr/article/details/92378336

(1)String类是不可变类,即一旦一个String对象被创建以后,包含在这个对象中的字符序列是不可改变的,直至这个对象被销毁。

String aaa="123";

aaa="456";

输出内容为456

上述行为 可以看到值被改变了,与我们之前的定义冲突,但是本次操作未改变原有的堆实例对象的值,而是重新指向最新生成的实例对象。

(2)StringBuffer对象则代表一个字符序列可变的字符串,当一个StringBuffer被创建以后,通过StringBuffer提供的append()、insert()、reverse()、setCharAt()、setLength()等方法可以改变这个字符串对象的字符序列。一旦通过StringBuffer生成了最终想要的字符串,就可以调用它的toString()方法将其转换为一个String对象。

StringBuffer adb=new StringBuffer("123");

adb.append("456");

所以说StringBuffer对象是一个字符序列可变的字符串,它没有重新生成一个对象,而且在原来的对象中可以连接新的字符串.

(3)StringBuilder类也代表可变字符串对象。实际上,StringBuilder和StringBuffer基本相似,两个类的构造器和方法也基本相同。不同的是:StringBuffer是线程安全的,而StringBuilder则没有实现线程安全功能,所以性能略高。

StringBuffer类中的方法都添加了synchronized关键字,也就是给这个方法添加了一个锁,用来保证线程安全。

3、HashMap的源码,实现原理,底层结构

HashMap就是数组+链表(哈希表或者散列函数)的组合实现,每个数组元素存储一个链表的头结点,本质上来说是哈希表“拉链法”的实现。

拉链法详解:

1)拉链法解决冲突的做法是:将所有关键字为同义词的结点链接在同一个单链表中。若选定的散列表长度为m,则可将散列表定义为一个由m个头指针组成的指针数组T[0..m-1]。凡是散列地址为i的结点,均插入到以T[i]为头指针的单链表中。T中各分量的初值均应为空指针。在拉链法中,装填因子α可以大于1,但一般均取α≤1

2)拉链法的优点

 与开放定址法相比,拉链法有如下几个优点:

  (1)拉链法处理冲突简单,且无堆积现象,即非同义词决不会发生冲突,因此平均查找长度较短;

  (2)由于拉链法中各链表上的结点空间是动态申请的,故它更适合于造表前无法确定表长的情况;

  (3)开放定址法为减少冲突,要求装填因子α较小,故当结点规模较大时会浪费很多空间。而拉链法中可取α≥1,且结点较大时,拉链法中增加的指针域可忽略不计,因此节省空间;

  (4)在用拉链法构造的散列表中,删除结点的操作易于实现。只要简单地删去链表上相应的结点即可。而对开放地址法构造的散列表,删除结点不能简单地将被删结点的空间置为空,否则将截断在它之后填人散列表的同义词结点的查找路径。这是因为各种开放地址法中,空地址单元(即开放地址)都是查找失败的条件。因此在用开放地址法处理冲突的散列表上执行删除操作,只能在被删结点上做删除标记,而不能真正删除结点。

3)拉链法的缺点是:指针需要额外的空间,故当结点规模较小时,开放定址法较为节省空间,而若将节省的指针空间用来扩大散列表的规模,可使装填因子变小,这又减少了开放定址法中的冲突,从而提高平均查找速度。

上图为散列列表的表达式。开闭散列的原理有待深究

capacity:容量

loadFactor:装载因子 默认为0.75

threshold:最多容纳Entry的个数 threshold=capacity*loadFactor,如果元素多于,进行*2扩增。

4)hashMap get的工作原理

使用put(key,value)方法,调用hashCode方法,返回hashCode值,获取存储bucket位置来存储Entry(K,V)对象

从数组通过hash值取得链表头,然后通过equals比较key,如果相同就复制原来的值,并返回老值。否则新增一个entry,返回null。新增的元素为链表头,以前相同数组位置的挂在后面

新增超值,resize,重新分配数组处理,将老数组数据转移新数组。

 

4、ArrayList、Vector和LinkedList的区别

1)ArrayList是最常用的List实现类,内部是通过数组实现的,它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要讲已经有数组的数据复制到新的存储空间中。当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。因此,它适合随机查找和遍历,不适合插入和删除。

优点:允许元素进行快速随机访问,适合随机查找和遍历

缺点:每个元素之间不能有间隔,数组大小不满足时需增加存储,在中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。最终结论不适合插入和删除。是线程不安全的

2)Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一个线程能够写Vector,避免多线程同时写而引起的不一致性,但实现同步需要很高的花费,因此,访问它比访问ArrayList慢

优点:支持线程的同步,是线程安全的

缺点:访问速度较ArrayList慢

3)LinkedList使用双向链表实现存储(将内存中零散的内存单元通过附加的引用关联起来,形成一个可以按序号索引的线性结构,这种链式存储方式与数组的连续存储方式相比,内存的利用率更高),按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。

优点:由于双向链表按序号索引数据进行向前或向后遍历,故而插入和删除速度较快

缺点:随机访问的get和set方法,ArrayList要优于LinkedList,因为LinkedList要移动指针

5、HashMap的hash碰撞问题

来源地址:https://blog.csdn.net/qq_35583089/article/details/80048285

1)什么是hash碰撞?

如果两个输入串的hash函数的值一样,则称这两个串是一个碰撞(Collision)。既然是把任意长度的字符串变成固定长度的字符串,所以必有一个输出串对应无穷多个输入串,碰撞是必然存在的。

一个优良的hash函数 f 应当满足以下三个条件:

 

(1)对于任意y,寻找x,使得f(x)=y,在计算上是不可行的。

 

(2)给定x1∈A,找x2∈B,,使得f(x1)=f(x2),在计算上是不可能的,这也就是弱无碰撞性。

 

(3)寻找x1,x2,使得f(x1)=f(x2),在计算上也是不可行的,这也就是强无碰撞性。

 

这样就称为安全保密的Hash函数,除了枚举外不可能有别的更快的方法。如第3条,根据生日定理,要想找到这样的x1,x2,理论上需要大约2^(n/2)的枚举次数。

因为前两条都能被破坏的hash函数太弱而被抛弃,几乎所有的hash函数的破解,都是指的破坏上面的第3条性质,即找到一个碰撞。在密码学上还有一个概念是理论破解,指的是提出一个算法,使得可以用低于理论值得枚举次数找到碰撞。

2)碰撞处理

(1)开放寻址法

所有的元素都在散列表中,每一个表项或包含动态集合的一个元素,或包含NIL。这种方法中散列表可能被填满,以致于不能插入任何新的元素。在开放寻址法中,当要插入一个元素时,可以连续地检查或探测散列表的各项,直到有一个空槽来放置待插入的关键字为止。有三种技术用于开放寻址法:线性探测、二次探测以及双重探测。

(2)链接法

将所有关键字为同义词的结点链接在同一个链表中。若选定的散列表长度为m,则可将散列表定义为一个由m个头指针组成的指针数组T[0..m-1]。凡是散列地址为i的结点,均插入到以T[i]为头指针的单链表中。T中各分量的初值均应为空指针。在拉链法中,装填因子α可以大于1,但一般均取α≤1。

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值