1. 简单说说java中的集合类
2. HashTable和HashMap的区别
3. HashTable和HashMap取哈希的方式有什么不同
4. HashMap的底层?
5.HashMap在jdk1.7和1.8的区别是什么
6.为什么HashMap负载因子知道吗,说说
7.负载因子为什么是0.75,大了或者小了会怎么样
8.Hashmap的初始容量设置是多少,如果要自定义,有什么要求?
8.HashMap扩容方式
9.有10万个数据用ArrayList怎么存?
10 .TreeMap的底层?
11.AVL树的最大高度差是多少?
12.说说B+树
13.其他数据结构了解吗?说说例子
用栈取链表的倒数第n个接点
还有用栈实现逆波兰计算器
14.jdk1.7的HashMap有什么缺点?
HashCode相同时是前插法,高并发的时候会造成死循环,还有1.8的HashMap在链表挂到第8个时会转红黑树
15.AVL树和二叉树的区别是什么?
能左旋和右旋,保证查询效率
16.jdk1.8的新特性?
答jvm,lambda表达式,和接口的默认方法这几个方面,当然还有刚刚提到的HashMap
17. 四大函数式接口,说说区别和用法
18.接口的默认和静态方法?
19.函数式接口的使用条件?
20.说说cloneable接口
21.说说对微服务的理解
22.数据库中UNION 和 UNION ALL的区别
23.try catch finally执行顺序,各种情况
24.同步代码块,还有静态同步代码块,面试官出了两道题,然后我来回答能不能执行
25.final关键字的作用?
26.ArrayList和LinkedList的区别
27.ArrayList最大容量
28.ArrayList的扩容方式
29. 如果TreeMap底层是普通的二叉查找树,在效率上会出现什么问题?
30.用英语介绍一下自己
下面作出回答,可能回答的比较浅,但是,只能先说这么多了。
1.
Collection接口的子接口包括:Set接口和List接口
Map接口的实现类主要有:HashMap、TreeMap、Hashtable、ConcurrentHashMap以及Properties等
Set接口的实现类主要有:HashSet、TreeSet、LinkedHashSet等
List接口的实现类主要有:ArrayList、LinkedList、Stack以及Vector等
2.
HashMap没有考虑同步,是线程不安全的;Hashtable使用了synchronized关键字,是线程安全的;
HashMap允许K/V都为null;后者K/V都不允许为null;
HashMap继承自AbstractMap类;而Hashtable继承自Dictionary类;
3.
Hashtable计算hash是直接使用key的hashcode对table数组的长度直接进行取模
HashMap计算hash对key的hashcode进行了二次hash,以获得更好的散列值,然后对table数组长度取摸。
我就看到一点,hashtable已经被淘汰掉了,难怪我没哟用过。
如果需要线程安全,请使用ConcurrentHashMap
4.
HashMap采用Entry数组来存储key-value对,每一个键值对组成了一个Entry实体,Entry类实际上是一个单向的链表结构,它具有Next指针,可以连接下一个Entry实体,以此来解决Hash冲突的问题。
数组存储区间是连续的,占用内存严重,故空间复杂的很大。但数组的二分查找时间复杂度小,为O(1);数组的特点是:寻址容易,插入和删除困难;
链表存储区间离散,占用内存比较宽松,故空间复杂度很小,但时间复杂度很大,达O(N)。链表的特点是:寻址困难,插入和删除容易。
数组+链表+红黑树
5.
最大区别就在于,链表长度阈值超过8,采用红黑树
还有就是
插入数据方式 | 头插法(先讲原位置的数据移到后1位,再插入数据到该位置) | 尾插法(直接插入到链表尾部/红黑树) |
6.
负载因子的影响
我们都知道有序数组存储数据,对数据的索引效率都很高,但是插入和删除就会有性能瓶颈(回忆ArrayList),
链表存储数据,要一次比较元素来检索出数据,所以索引效率低,但是插入和删除效率高(回忆LinkedList),
两者取长补短就产生了哈希散列这种存储方式,也就是HashMap的存储逻辑.
而负载因子表示一个散列表的空间的使用程度,有这样一个公式:initailCapacity*loadFactor=HashMap的容量。
所以负载因子越大则散列表的装填程度越高,也就是能容纳更多的元素,元素多了,链表大了,所以此时索引效率就会降低。
反之,负载因子越小则链表中的数据量就越稀疏,此时会对空间造成烂费,但是此时索引效率高。
7.
为什么是0.75 深挖下去其实是统计学了
记住泊淞分布就好了,再深挖就要挖到统计学那边去了,就此打住,重申一下使用hash容器请尽量指定初始容量,且是2的幂次方。
8.
HashMap有一个初始容量大小,默认是16,
扩容方式 resize()
9.
因为arraylist底层为数组,数组默认长度为10,如果一个个加进去,要不断扩容。
这样非常耗费时间,所以我们调用ArrayList的指定容量的构造器方法ArrayList(int size) 就可以实现不扩容,就提高了性能。
10.
treemap底层为 红黑树
红黑树又称红-黑二叉树,它首先是一颗二叉树,它具体二叉树所有的特性。同时红黑树更是一颗自平衡的排序二叉树。
11.
AVL树中任何节点的两个子树的高度最大差别为1
这个我没有搜索资料都知道肯定是1
因为原理就是为了寻找平衡作用
12.
B+树是的多路平衡树
最重要的概念,记住:b-树每个节点中不仅有key还有data,数据一单大且多会占据空间,增大io;b+树,叶子节点仅有key,和辅助会记录一个磁盘地址,这样来优化内部空间,减少io
13.其他数据结构部有了解吗,说一说
这个可以说一说链表
14.
1.7hashMap缺点就是,前插法,高并发会死循环。链表数据过多,查询深度增加,查询较慢。
15.
avl树和二叉树区别
就是可以旋转,左旋或者右旋,优化寻址深度,更快查询
16.
jdk1.8新特性,就是函数式变成,lambda,还有hashmap的优化
17.
这个第一次听到。
供给型接口
Supplier<T> 返回T类型对象
T get();
Supplier<Apple> supplier = () -> new Apple();
// Supplier<Apple> supplier = Apple::new;
supplier.get();
消费型接口
Consumer<T> 接收一个 T 类型
void accept(T t);
List<String> languages = Arrays.asList("java","scala","python");
Consumer<String> consumer = System.out::println;
languages.stream().forEach(consumer);
函数型接口
Function<T, R> 由T类型对象转成R类型对象
R apply(T t);
List<String> languages = Arrays.asList("java","scala","python");
Function<String, Integer> ti = String::length;
languages.stream()
.map(ti)
.forEach(System.out::println);
断言型接口
Predicate<T> 条件判断
boolean test(T t);
List<String> languages = Arrays.asList("java","scala","python");
Predicate<String> p = (str) -> str.length() > 4;
languages.stream()
.filter(p)
.forEach(System.out::println);
18.
这个也是第一次听说
默认方法
使用default修饰,不可省略,供子类调用或者子类重写
可以继承,可以重写,二选一,但是只能通过实现类的对象来调用
接口中,有多个默认方法时,实现类都可继承使用,如果继承方法有重名的,必须重写一次
5.静态方法
使用static修饰,供接口直接调用
静态与.class文件相关,只能使用接口名调用,不可以通过实现类的类名或者实现类的对象调用
19. 20.
真的没有用过,不能再说了,再说就是死记硬背了,没有体会死记硬背没有意义,也不可取。要不遇到了再说吧
21.
注册中心 (Eureka)、配置中心 (Config)、断路器 (Hytrix)、API 网关 (Zuul) 等组件
服务的粒度,切分到多大算合适? 太粗的话,这服务就涵盖过多的业务逻辑,从而难维护、易出错;太细了,就会搞出很多的工程,造成很大的工程维护和通信成本。
一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)
一致性(C):在分布式系统中的所有数据备份,在同一时刻是否同样的值。(等同于所有节点访问同一份最新的数据副本)
可用性(A):在集群中一部分节点故障后,集群整体是否还能响应客户端的读写请求。(对数据更新具备高可用性)
分区容忍性(P):以实际效果而言,分区相当于对通信的时限要求。系统如果不能在时限内达成数据一致性,就意味着发生了分区的情况,必须就当前操作在C和A之间做出选择。
CAP(Consistence、Available、Partition) 理论是很熟悉的分布式理论——在同一时间 CAP 不能全部满足、只能满足其中两个。而由于分布式系统的特点,Partition 是必须要满足的,所以只能要么 CP、要么 AP,即要么系统可用,但数据可能不一致;要么数据一致,但系统不可用。
因此在进行分布式架构设计时,必须做出取舍。
22.
union会自动压缩多个结果集合中的重复结果,而union all则将所有的结果全部显示出来。
union all是直接连接,取到得是所有值,记录可能有重复;union 是取唯一值,记录没有重复。所以union在进行表链接后会筛选掉重复的记录,union all不会去除重复记录。
union将会按照字段的顺序进行排序;union all只是简单的将两个结果合并后就返回。从效率上说,union all 要比union快很多,所以,如果可以确认合并的两个结果集中不包含重复数据且不需要排序时的话,那么就使用union all。
23.
总结:
finally中的代码总会被执行。
当try、catch中有return时,也会执行finally。return的时候,要注意返回值的类型,是否受到finally中代码的影响。
finally中有return时,会直接在finally中退出,导致try、catch中的return失效。
24.
这里我想拓展一个 指令重排的概念
JMM(Java Memory Model)中的主存, 工作内存以及数据如何在其中流转等等,
这些本身还牵扯到硬件内存架构, 直接上手容易绕晕, 先从以下几个点探索JMM
- 原子性
- 有序性
- 可见性
- 指令重排
volatile规则
volatile变量的写,先发生于读,这保证了volatile变量的可见性,
volatile 可以同步,但是性能会变慢。
25.
final
1,final修饰的class,代表不可以继承扩展。
2、final的方法也是不可以重写的。
3、final修饰的变量是不可以修改的。这里所谓的不可修改对于基本类型来来,的确是不可以修改。而对于引用类型来说,只能说不能重新赋值。也就是不能改变引用地址。但是作为引用类型,它内部所包含的内容如果不是final则可以随意修改
finally
提到finally,那么try-catch就逃不掉了。finally 则是Java保证重点代码一定要被执行的一种机制。最常用的地方:通过try-catch-finally来进行类似资源释放、保证解锁等动作
finalize
设计之初的作用就是:在CG要回收某个对象时,让这个对象有底气的大喊一声:“报告,我还能再抢救一下!”。但是也正是因为如此,JVM要对它进行额外处理。finalize也就成为了CG回收的阻碍者,也就会导致这个对象经过多个垃圾收集周期才能被回收。
26.
- 1、ArrayList继承了AbstractList类,而LinkList继承了AbstractSequentialList类,而AbstractSequentialLis继承了AbstractList
LinkList继承的AbstractSequentialList实现了get(int index)、set(int index, E element)、add(int index, E element) 和 remove(int index)这些骨干性函数,
降低了List接口的复杂度。 - 2、ArrayList和LinkList的内部实现的数据结构不同
ArrayList内部是由数组是实现的,而LinkList内部是由循环双向链表实现的。
由于ArrayList是由数组实现的,所以ArrayList在进行查找操作时,速度要优于由链表实现的LinkList,
但是在进行删除添加操作时,LinkList速度要优于ArrayList
所以当进行查找操作更多时,使用ArrayList,而如果进行插入和删除操作更多时,使用LinkList - 3、LinkList需要更多的内存空间,因为它除了要存储数据之外,还需要存储该节点的前后节点信息,而ArrayList索引处就是存的数据
27,28
有一个DEFAULT_CAPACITY变量,大小为10
最大上限是Integer.MAX_VALUE
list默认大小为10,如果手动设置了初始化大小,list的默认大小就是自己设置的那个值;
list大小为n,插入第n个元素时才会触发扩容;
扩容上限为int的最大值;
29.
其实就是回答普通二叉树的劣势,那肯定是链路深度了,太深了,所以慢。
普通二叉树的问题在于,查询链路深度较长,查询时间复杂度成本高。
30.
这个我觉得还是看平时积累,像我大学用,毕业就只是看英文文档时候用,平时很少说,这个就大家拼了,看学生时代的兴趣爱好了。
拓展知识:
1.currentHashMap总结
由于HashMap是线程不同步的,虽然处理数据的效率高,但是在多线程的情况下存在着安全问题,因此设计了CurrentHashMap来解决多线程安全问题。
HashMap在put的时候,插入的元素超过了容量(由负载因子决定)的范围就会触发扩容操作,就是rehash,这个会重新将原数组的内容重新hash到新的扩容数组中,在多线程的环境下,存在同时其他的元素也在进行put操作,如果hash值相同,可能出现同时在同一数组下用链表表示,造成闭环,导致在get时会出现死循环,所以HashMap是线程不安全的。
直接用Node数组+链表+红黑树的数据结构来实现,并发控制使用Synchronized和CAS来操作,整个看起来就像是优化过且线程安全的HashMap,虽然在JDK1.8中还能看到Segment的数据结构,但是已经简化了属性,只是为了兼容旧版本.