高级会员
- 威望
- 371
- 贡献
- 478
- 热心值
- 0
- 金币
- 19
- 注册时间
- 2020-3-29
|
1. linkedList与arrayList区别 适用场景
• 区别:
a. 数据结构不同,ArrayList是基于数组实现的,LinkedList是基于双向链表实现的;
b. LinkedList不仅是List接口的实现类,可以根据索引来随机访问集合中的元素,另外,LinkedList还实现了Deque接口,Deque接口是Queue接口的子接口,它代表一个双向队列,因此LinkList可以作为双向队列、栈和List集合使用;
c. 因为ArrayList是基于索引的数据结构,它使用索引在数组中搜索和读取数据是很快的,可以直接返回数组中索引位置的元素,因此在随机访问集合元素上有较好的性能。ArrayList获取数据的时间复杂度是O(1),但是要插入、删除数据却是开销很大,因为这需要移动数组中插入位置之后的所有元素;
d. 相比于ArrayList,LinkedList的随机访问集合元素时性能较差,因为需要对双向链表进行遍历;但在插入,删除操作是更快的,因为LinkedList不像ArrayList,不需要改变数组的大小,也不需要在数组装满的时候将所有的数组重新装入一个新的数组,这是ArrayList最坏的一种情况,时间复杂度是O(n),而LinkedList中插入或删除的时间复杂度仅为O(1)。
e. LinkedList需要更多的内存,因为ArrayList的每个索引的位置存储的是实际的数据,而LinkedList的每个节点中存储的是实际的数据和前后节点的地址。
• 使用场景:
a. 如果应用程序对数据有较多的随机访问,ArrayList对象要优于LinkedList;
b. 如果应用程序有更多的插入或者删除操作,较少的数据读取,LinkedList对象要优于ArrayList对象;
c. 不过,ArrayList的插入,删除操作也不一定比LinkedList慢,如果在ArrayList靠近末尾的地方插入,那么ArrayList只需要移动较少的数据,而LinkedList则需要一直查找到列表尾部,反而耗费较多时间,这时ArrayList要比LinkedList要快。
2. ArrayList和LinkedList的插入和访问的时间复杂度?
• ArrayList 是线性表(数组)
○ get() 直接读取第几个下标,复杂度 O(1)
○ add(E) 添加元素,直接在后面添加,复杂度O(1)
○ add(index, E) 添加元素,在第几个元素后面插入,后面的元素需要向后移动,复杂度O(n)
○ remove()删除元素,后面的元素需要逐个移动,复杂度O(n)
• LinkedList 是链表的操作
○ get() 获取第几个元素,依次遍历,复杂度O(n)
○ add(E) 添加到末尾,复杂度O(1)
○ add(index, E) 添加第几个元素后,需要先查找到第几个元素,直接指针指向操作,复杂度O(n)
○ remove()删除元素,直接指针指向操作,复杂度O(1)
• 当操作是在一列数据的后面添加数据而不是在前面或中间,并且需要随机地访问其中的元素时,使用ArrayList会提供比较好的性能;当你的操作是在一列数据的前面或中间添加或删除数据,并且按照顺序访问其中的元素时,就应该使用LinkedList了。
3. ArrayList是如何扩容的
扩容原来数组的1.5倍,并把老数组copy到新数。
4. ArrayList是否会越界?
会。
○ 调用空参构造函数创建对象后,直接调用add(index,element)方法时会引发数据越界异常;
○ 多线程并发情况下也有可能出现越界异常;
5. hashset存的数是有序的么?
HashSet的元素无序不可重复。
6. ArrayList和hashset有何区别?
○ ArrayList实现的是List接口,HashSet实现的是Set接口
○ ArrayList存储的元素有序可重复,HashSet存储的元素无序不可重复
○ ArrayList底层实现是基于数组,HashSet底层实现是基于哈希表
7. 说说HaspMap底层原理?
HashMap基于Hash算法,我们通过put(key,value)存储,get(key)来获取。当传入key时,HashMap会根据key.hashCode()计算出hash值,根据hash值将value保存在bucket里。当计算出的hash值相同时,我们称之为Hash冲突,HashMap的做法是用链表和红黑树存储相同hash值的value。当Hash冲突的个数比较少时,使用链表,否则使用红黑树。
8. Java8中的HashMap有什么变化?
○ 底层数据结构不同:1.7是数组+链表,1.8是数组+链表+红黑树
○ 插入键值对的方式不同:1.7中采用头插法,1.8中采用尾插法
○ 哈希表初始化方式不同:1.7中会先初始化一个数组,而1.8中是在首次put时候才会调用resize方法进行扩容
○ hash函数对哈希值的计算方式不同:1.7中直接计算key的hashCode值,1.8中则采用key的hashCode异或上key的hashCode进行无符号右移16位的结果,避免了只靠低位数据来计算哈希时导致的冲突,计算结果由高低位结合决定,使元素分配更均匀
○ 扩容策略不同:1.7中是只要不小于阈值就直接扩容2倍;而1.8的扩容策略会更优化,当数组容量未达到64时,以2倍进行扩容,超过64之后若桶中元素个数不小于7就将链表转换为红黑树,但如果红黑树中的元素个数小于6就会还原为链表
9. HashMap的底层数据结构
○ 1.7是数组+链表
○ 1.8是数组+链表+红黑树
10. 1.8还采用了红黑树,讲讲红黑树的特性
1,每个节点要么是红色,要么是黑色。
2,根节点必须是黑色。叶子节点必须是黑色NULL节点。
3,红色节点不能连续。
4,对于每个节点,从该点至叶子节点的任何路径,都含有相同个数的黑色节点。
5,能够以O(log2(N))的时间复杂度进行搜索、插入、删除操作。此外,任何不平衡都会在3次旋转之内解决。
11. 为什么HashMap要用红黑树而不是AVL树?
红黑树的查询性能略微逊色于AVL树,因为其比AVL树会稍微不平衡最多一层,也就是说红黑树的查询性能只比相同内容的AVL树最多多一次比较,但是,红黑树在插入和删除上优于AVL树,AVL树每次插入删除会进行大量的平衡度计算,而红黑树为了维持红黑性质所做的红黑变换和旋转的开销,相较于AVL树为了维持平衡的开销要小得多
12. 红黑树需要比较大小才能进行插入,是依据什么进行比较的?
是根据插入节点的hash值进行比较的
13. HashMap的时间复杂度?
• 不管插入还是查找,由key获取hash值然后定位到桶的时间复杂度都是O(1)
• 真正决定时间复杂度的实际上是桶里面链表/红黑树的情况:
○ 如果桶里面没有元素,那么直接将元素插入/或者直接返回未查找到,时间复杂度就是O(1)
○ 如果里面有元素,那么就沿着链表进行遍历,时间复杂度就是O(n),链表越短时间复杂度越低
○ 如果是红黑树的话那就是O(logn)
14. HashMap如何解决Hash冲突?
采用的是链表法,将相同hash值的对象组织成一个链表放在hash值对应的槽位,JDK18时,会在链表长度超过8时,把链表转化成红黑树,后续的节点插入到红黑树中
15. HashMap检测到hash冲突后,将元素插入在链表的末尾还是开头?
1.7中采用头插法,1.8中采用尾插法
16. HashMap哈希函数的认识,JDK1.8采用的hash函数
1.7中直接计算key的hashCode值,1.8中则采用key的hashCode异或上key的hashCode进行无符号右移16位的结果,避免了只靠低位数据来计算哈希时导致的冲突,计算结果由高低位结合决定,使元素分配更均匀
17. HashMap怎么从链表转换为红黑树?
当数组长度大于64,且当链表已经有8个节点了,此时再新链上第9个节点,在成功添加了这个新节点之后,立马做链表转红黑树
18. HashMap如果存入的是null键,放在桶的哪个位置?
存在数组的第一个位置。
19. HashMap的put操作讲一下
a. 取key的hashCode进行高位运算,返回hash值
b. 如果数组为空,直接调用resize方法进行扩容
c. 如果数组不为空,对hash进行取模运算,得到数组的下标位置
d. 判断对应数组下标位置是否为空,为空则直接插入Node节点
e. 不为则判断是否为红黑树
f. 如果是红黑树,则判断TreeNode节点是否已经存在,如果存在,则直接返回原节点数据,并更新;不存在,则直接插入红黑树,超出负载容量就扩容
g. 如果是链表,则判断Node节点是否已经存在,如果存在,则直接返回原节点数据,并更新;不存在则直接插入链表尾部,然后判断链表长度,如果大于8则转为红黑树存储,超出负载容量就扩容
20. HashMap中的get()方法是如何实现的?
a. 对key进行哈希计算
b. 通过数组长度减一与hash值进行取模运算获取该key在数组的位置
c. 判断数组中的首节点是否为空,为空则直接返回空
d. 如果不为空,再判断节点key是否和目标值相同,相同则直接返回,不相同则判断节点的next引用是否为空,为空则直接返回空
e. 如果节点的next引用不为空,则判断是否为红黑树节点,如果是红黑树,则进入红黑树的取值流程,并返回结果
f. 如果是链表,则进入链表的取值流程,并返回结果
取值流程:key的哈希值相等并且key的值也相等
|
|