为工作而记(1)

2018-03-21  本文已影响0人  daidai呆呆7

1.面试题:你说说collection里面有什么子类

1.List 和 Set 的区别

list

1.可以允许重复的对象

2.可以插入多少null元素

3.是一个有序的容器,保持了每个元素的插入顺序,输出的顺序就是插入的顺序

4.常用的实现类有ArryList、LinkedList和Vector.ArrayList最为流行,它提供了使用索引的随意访问,而LinkedList则对于经常

需要从List中添加或删除元素的场合更为合适

set

1.不允许重复对象

2.无序容器,你无法保证每个元素的存储顺序,TreeSet通过Comparator或者Comparable维护了一个排序顺序

3.只允许一个null元素

4.Set接口最流行的几个实现类是HashSet、LinkedHashSet以及TreeSet.最流行的是基于HashMap实现的HashSet;TreeSet还实现了

SortedSet接口,因此TreeSet是一个根据compare()和compareTo()的定义进行排序的有序容器

map

1.Map不是conllection的子接口或者实现类,Map是一个接口。

2.Map的每个Entry都持有两个对象,也是一个键一个值,Map可能会有持有相同的值对象但键对象必须是唯一的。

3.TreeMap也是通过Comparator或者Comparable维护了一个排序须序。

4.Map里你可以拥有随意个null值但最多只能有一个null键。

5.Map接口最流行的几个实现类是HashMap、LinkedHashMap、HashTable和TreeMap(HashMap、TreeMap最常用)

2.面试题:什么场景下使用list,set,map呢?

(或者会问为什么这里要用list、或者set、map,这里回答它们的优缺点就可以了)

1.如果你经常会使用索引来对容器中的元素进行访问,那么 List 是你的正确的选择。如果你已经知道索引了的话,那么 List 的实现类比如 ArrayList 可以提供更快速的访问,如果经常添加删除元素的,那么肯定要选择LinkedList。

2.如果你想容器中的元素能够按照它们插入的次序进行有序存储,那么还是 List,因为 List 是一个有序容器,它按照插入顺序进行存储。

3.如果你想保证插入元素的唯一性,也就是你不想有重复值的出现,那么可以选择一个 Set 的实现类,比如 HashSet、LinkedHashSet 或者 TreeSet。所有 Set 的实现类都遵循了统一约束比如唯一性,而且还提供了额外的特性比如 TreeSet 还是一个 SortedSet,所有存储于 TreeSet 中的元素可以使用 Java 里的 Comparator 或者 Comparable 进行排序。LinkedHashSet 也按照元素的插入顺序对它们进行存储。

4.如果你以键和值的形式进行数据存储那么 Map 是你正确的选择。你可以根据你的后续需要从 Hashtable、HashMap、TreeMap 中进行选择。

3.HashSet 是如何保证不重复的

HashSet类是如何实现添加元素保证不重复的---哈希码的原理

4.HashMap 是线程安全的吗,为什么不是线程安全的

HashTable是线程安全的,因为里面的方法使用了synchronized进行同步,但是HashMap没有,所以HashMap是非线程安全的

HashMap底层是一个Entry数组,当发生hash冲突的时候,HashMap是采用链表的方式来解决的,在对应的数组位置存放链表的头结点

Map m = Collections.synchronizedMap(new HashMap(...));

5.HashMap在Java1.7与1.8中的区别

基于JDK1.7.0_80与JDK1.8.0_66做的分析

JDK1.7中

使用一个Entry数组来存储数据,用key的hashcode取模来决定key会被放到数组里的位置,如果hashcode相同,或者hashcode取模后的结果相同(hash collision),那么这些key会被定位到Entry数组的同一个格子里,这些key会形成一个链表。

在hashcode特别差的情况下,比方说所有key的hashcode都相同,这个链表可能会很长,那么put/get操作都可能需要遍历这个链表

也就是说时间复杂度在最差情况下会退化到O(n)

JDK1.8中

使用一个Node数组来存储数据,但这个Node可能是链表结构,也可能是红黑树结构

如果插入的key的hashcode相同,那么这些key也会被定位到Node数组的同一个格子里。

如果同一个格子里的key不超过8个,使用链表结构存储。

如果超过了8个,那么会调用treeifyBin函数,将链表转换为红黑树。

那么即使hashcode完全相同,由于红黑树的特点,查找某个特定元素,也只需要O(log n)的开销

也就是说put/get的操作的时间复杂度最差只有O(log n)

听起来挺不错,但是真正想要利用JDK1.8的好处,有一个限制:

key的对象,必须正确的实现了Compare接口

如果没有实现Compare接口,或者实现得不正确(比方说所有Compare方法都返回0)

那JDK1.8的HashMap其实还是慢于JDK1.7的

简单的测试数据如下:

向HashMap中put/get 1w条hashcode相同的对象

JDK1.7:                      put 0.26s,get 0.55s

JDK1.8(未实现Compare接口):put 0.92s,get 2.1s

但是如果正确的实现了Compare接口,那么JDK1.8中的HashMap的性能有巨大提升,这次put/get 100W条hashcode相同的对象

JDK1.8(正确实现Compare接口,):put/get大概开销都在320ms左右

为什么要这么操作呢?

我认为应该是为了避免Hash Collision DoS攻击

Java中String的hashcode函数的强度很弱,有心人可以很容易的构造出大量hashcode相同的String对象。

如果向服务器一次提交数万个hashcode相同的字符串参数,那么可以很容易的卡死JDK1.7版本的服务器。

但是String正确的实现了Compare接口,因此在JDK1.8版本的服务器上,Hash Collision DoS不会造成不可承受的开销。

6.HashMap碰撞

利用链表方式来解决,hashcode相同,hash是否相同,equals相同

7.final、finally、finalize区别

Final用于修饰类、成员变量和成员方法

final修饰的类,不能被继承(String、StringBuilder、StringBuffer、Math,不可变类),其中所有的方法都不能被重写,所以不能同时用abstract和final修饰类(abstract修饰的类是抽象类,抽象类是用于被子类继承的,和final起相反的作用)

Final修饰的方法不能被重写,但是子类可以用父类中final修饰的方法

Final修饰的成员变量是不可变的,如果成员变量是基本数据类型,初始化之后成员变量的值不能被改变,如果成员变量是引用类型,那么它只能指向初始化时指向的那个对象,不能再指向别的对象,但是对象当中的内容是允许改变的

Finally通常和try catch搭配使用,保证不管有没有发生异常,资源都能够被释放(释放连接、关闭IO流)

Finalize是object类中的一个方法,子类可以重写finalize()方法实现对资源的回收。垃圾回收只负责回收内存,并不负责资源的回收,资源回收要由程序员完成,Java虚拟机在垃圾回收之前会先调用垃圾对象的finalize方法用于使对象释放资源(如关闭连接、关闭文件),之后才进行垃圾回收,这个方法一般不会显示的调用,在垃圾回收时垃圾回收器会主动调用。

8.Java四种引用包括强引用,软引用,弱引用,虚引用

强引用:

只要引用存在,垃圾回收器永远不会回收

Object obj = new Object();

//可直接通过obj取得对应的对象 如obj.equels(new Object());

而这样 obj对象对后面new Object的一个强引用,只有当obj这个引用被释放之后,

对象才会被释放掉,这也是我们经常所用到的编码形式。

软引用:

非必须引用,内存溢出之前进行回收,可以通过以下代码实现

Object obj = new Object();

SoftReference<Object> sf = new SoftReference<Object>(obj);

obj = null;

sf.get();//有时候会返回null

这时候sf是对obj的一个软引用,通过sf.get()方法可以取到这个对象,当然,

当这个对象被标记为需要回收的对象时,则返回null;

软引用主要用户实现类似缓存的功能,在内存足够的情况下直接通过软引用取值,

无需从繁忙的真实来源查询数据,提升速度;当内存不足时,自动删除这部分缓存数据,从真正的来源查询这些数据

弱引用:

第二次垃圾回收时回收,可以通过如下代码实现

Object obj = new Object();

WeakReference<Object> wf = new WeakReference<Object>(obj);

obj = null;

wf.get();//有时候会返回null

wf.isEnQueued();//返回是否被垃圾回收器标记为即将回收的垃圾

弱引用是在第二次垃圾回收时回收,短时间内通过弱引用取对应的数据,可以取到,当执行过第二次垃圾回收时,将返回null。

弱引用主要用于监控对象是否已经被垃圾回收器标记为即将回收的垃圾,可以通过弱引用的isEnQueued方法返回对象是否被垃圾回收器标记。

虚引用:

垃圾回收时回收,无法通过引用取到对象值,可以通过如下代码实现

Object obj = new Object();

PhantomReference<Object> pf = new PhantomReference<Object>(obj);

obj=null;

pf.get();//永远返回null

pf.isEnQueued();//返回是否从内存中已经删除

虚引用是每次垃圾回收的时候都会被回收,通过虚引用的get方法永远获取到的数据为null,因此也被成为幽灵引用。

虚引用主要用于检测对象是否已经从内存中删除。

9.深入理解Java反射

Class.forName

10.Array.sort和Collections.sort实现原理解析

不论是Collections.sort方法或者是Arrays.sort方法,底层实现都是TimSort实现的,

这是jdk1.7新增的,以前是归并排序。

TimSort算法就是找到已经排好序数据的子序列,然后对剩余部分排序,然后合并起来

11.LinkedHashMap的应用 http://blog.csdn.net/kiss_the_sun/article/details/7848920

LinkedHashMap是Map接口的哈希表和链接列表实现,具有可预知的迭代顺序。

LinkedHashMap实现与HashMap的不同之处在于,LinkedHashMap维护着一个运行于

所有条目的双重链接列表。

此链接列表定义了迭代顺序,该迭代顺序可以是插入顺序(insert-order)或者是访问顺序,

其中默认的迭代访问顺序就是插入顺序,即可以按插入的顺序遍历元素,

这点和HashMap有很大的不同

LinkedHashMap的扩展应用

    基于LinkedHashMap的访问顺序的特点,可构造一个LRU(Least Recently Used)

最近最少使用简单缓存。也有一些开源的缓存产品如ehcache的淘汰策略(LRU)

就是在LinkedHashMap上扩展的

上一篇下一篇

猜你喜欢

热点阅读