久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

50道Java集合經(jīng)典面試題(收藏版)

 撿田螺的小男孩 2020-06-12

前言

來(lái)了來(lái)了,,50道Java集合面試題也來(lái)啦~ 已經(jīng)上傳github:

https://github.com/whx123/JavaHome

1. Arraylist與LinkedList區(qū)別

可以從它們的底層數(shù)據(jù)結(jié)構(gòu),、效率、開銷進(jìn)行闡述哈

  • ArrayList是數(shù)組的數(shù)據(jù)結(jié)構(gòu),,LinkedList是鏈表的數(shù)據(jù)結(jié)構(gòu),。
  • 隨機(jī)訪問(wèn)的時(shí)候,ArrayList的效率比較高,,因?yàn)長(zhǎng)inkedList要移動(dòng)指針,,而ArrayList是基于索引(index)的數(shù)據(jù)結(jié)構(gòu),可以直接映射到,。
  • 插入,、刪除數(shù)據(jù)時(shí),LinkedList的效率比較高,,因?yàn)锳rrayList要移動(dòng)數(shù)據(jù),。
  • LinkedList比ArrayList開銷更大,因?yàn)長(zhǎng)inkedList的節(jié)點(diǎn)除了存儲(chǔ)數(shù)據(jù),,還需要存儲(chǔ)引用,。

2. Collections.sort和Arrays.sort的實(shí)現(xiàn)原理

Collection.sort是對(duì)list進(jìn)行排序,Arrays.sort是對(duì)數(shù)組進(jìn)行排序,。

Collections.sort底層實(shí)現(xiàn)

Collections.sort方法調(diào)用了list.sort方法list.sort方法調(diào)用了Arrays.sort的方法因此,,Collections.sort方法底層就是調(diào)用的Array.sort方法

Arrays.sort底層實(shí)現(xiàn)

Arrays的sort方法,如下:如果比較器為null,,進(jìn)入sort(a)方法,。如下:因此,Arrays的sort方法底層就是:

  • legacyMergeSort(a),,歸并排序,,
  • ComparableTimSort.sort():即Timsort排序。

Timesort排序

Timsort排序是結(jié)合了合并排序(merge.sort)和插入排序(insertion sort)而得出的排序方法,;

1.當(dāng)數(shù)組長(zhǎng)度小于某個(gè)值,,采用的是二分插入排序算法,如下:

  1. 找到各個(gè)run,,并入棧,。
  1. 按規(guī)則合并run。

3. HashMap原理,,java8做了什么改變

  • HashMap是以鍵值對(duì)存儲(chǔ)數(shù)據(jù)的集合容器
  • HashMap是非線性安全的,。
  • HashMap底層數(shù)據(jù)結(jié)構(gòu):數(shù)組+(鏈表、紅黑樹),,jdk8之前是用數(shù)組+鏈表的方式實(shí)現(xiàn),,jdk8引進(jìn)了紅黑樹
  • Hashmap數(shù)組的默認(rèn)初始長(zhǎng)度是16,key和value都允許null的存在
  • HashMap的內(nèi)部實(shí)現(xiàn)數(shù)組是Node[]數(shù)組,,上面存放的是key-value鍵值對(duì)的節(jié)點(diǎn),。HashMap通過(guò)put和get方法存儲(chǔ)和獲取,。
  • HashMap的put方法,首先計(jì)算key的hashcode值,,定位到對(duì)應(yīng)的數(shù)組索引,,然后再在該索引的單向鏈表上進(jìn)行循環(huán)遍歷,用equals比較key是否存在,,如果存在則用新的value覆蓋原值,,如果沒有則向后追加。
  • jdk8中put方法:先判斷Hashmap是否為空,,為空就擴(kuò)容,,不為空計(jì)算出key的hash值i,然后看table[i]是否為空,,為空就直接插入,,不為空判斷當(dāng)前位置的key和table[i]是否相同,相同就覆蓋,,不相同就查看table[i]是否是紅黑樹節(jié)點(diǎn),,如果是的話就用紅黑樹直接插入鍵值對(duì),如果不是開始遍歷鏈表插入,,如果遇到重復(fù)值就覆蓋,,否則直接插入,如果鏈表長(zhǎng)度大于8,,轉(zhuǎn)為紅黑樹結(jié)構(gòu),執(zhí)行完成后看size是否大于閾值threshold,,大于就擴(kuò)容,,否則直接結(jié)束。
  • Hashmap解決hash沖突,,使用的是鏈地址法,,即數(shù)組+鏈表的形式來(lái)解決。put執(zhí)行首先判斷table[i]位置,,如果為空就直接插入,,不為空判斷和當(dāng)前值是否相等,相等就覆蓋,,如果不相等的話,,判斷是否是紅黑樹節(jié)點(diǎn),如果不是,,就從table[i]位置開始遍歷鏈表,,相等覆蓋,不相等插入,。
  • HashMap的get方法就是計(jì)算出要獲取元素的hash值,,去對(duì)應(yīng)位置獲取即可,。
  • HashMap的擴(kuò)容機(jī)制,Hashmap的擴(kuò)容中主要進(jìn)行兩步,,第一步把數(shù)組長(zhǎng)度變?yōu)樵瓉?lái)的兩倍,,第二部把舊數(shù)組的元素重新計(jì)算hash插入到新數(shù)組中,jdk8時(shí),,不用重新計(jì)算hash,,只用看看原來(lái)的hash值新增的一位是零還是1,如果是1這個(gè)元素在新數(shù)組中的位置,,是原數(shù)組的位置加原數(shù)組長(zhǎng)度,,如果是零就插入到原數(shù)組中。擴(kuò)容過(guò)程第二部一個(gè)非常重要的方法是transfer方法,,采用頭插法,,把舊數(shù)組的元素插入到新數(shù)組中。
  • HashMap大小為什么是2的冪次方,?效率高+空間分布均勻

有關(guān)于HashMap這些常量設(shè)計(jì)目的,,也可以看我這篇文章:面試加分項(xiàng)-HashMap源碼中這些常量的設(shè)計(jì)目的

4. List 和 Set,Map 的區(qū)別

  • List 以索引來(lái)存取元素,,有序的,,元素是允許重復(fù)的,可以插入多個(gè)null,。
  • Set 不能存放重復(fù)元素,,無(wú)序的,只允許一個(gè)null
  • Map 保存鍵值對(duì)映射,,映射關(guān)系可以一對(duì)一,、多對(duì)一
  • List 有基于數(shù)組、鏈表實(shí)現(xiàn)兩種方式
  • Set,、Map 容器有基于哈希存儲(chǔ)和紅黑樹兩種方式實(shí)現(xiàn)
  • Set 基于 Map 實(shí)現(xiàn),,Set 里的元素值就是 Map的鍵值

5. poll()方法和 remove()方法的區(qū)別?

Queue隊(duì)列中,,poll() 和 remove() 都是從隊(duì)列中取出一個(gè)元素,,在隊(duì)列元素為空的情況下,remove() 方法會(huì)拋出異常,,poll() 方法只會(huì)返回 null ,。

看一下源碼的解釋吧:

/** * Retrieves and removes the head of this queue. This method differs * from {@link #poll poll} only in that it throws an exception if this * queue is empty. * * @return the head of this queue * @throws NoSuchElementException if this queue is empty */    remove(); /** * Retrieves and removes the head of this queue, * or returns {@code null} if this queue is empty. * * @return the head of this queue, or {@code null} if this queue is empty */ E poll();

6. HashMap,HashTable,,ConcurrentHash的共同點(diǎn)和區(qū)別

HashMap

  • 底層由鏈表+數(shù)組+紅黑樹實(shí)現(xiàn)
  • 可以存儲(chǔ)null鍵和null值
  • 線性不安全
  • 初始容量為16,,擴(kuò)容每次都是2的n次冪
  • 加載因子為0.75,當(dāng)Map中元素總數(shù)超過(guò)Entry數(shù)組的0.75,觸發(fā)擴(kuò)容操作.
  • 并發(fā)情況下,,HashMap進(jìn)行put操作會(huì)引起死循環(huán),,導(dǎo)致CPU利用率接近100%
  • HashMap是對(duì)Map接口的實(shí)現(xiàn)

HashTable

  • HashTable的底層也是由鏈表+數(shù)組+紅黑樹實(shí)現(xiàn)。
  • 無(wú)論key還是value都不能為null
  • 它是線性安全的,,使用了synchronized關(guān)鍵字,。
  • HashTable實(shí)現(xiàn)了Map接口和Dictionary抽象類
  • Hashtable初始容量為11

ConcurrentHashMap

  • ConcurrentHashMap的底層是數(shù)組+鏈表+紅黑樹
  • 不能存儲(chǔ)null鍵和值
  • ConcurrentHashMap是線程安全的
  • ConcurrentHashMap使用鎖分段技術(shù)確保線性安全
  • JDK8為何又放棄分段鎖,是因?yàn)槎鄠€(gè)分段鎖浪費(fèi)內(nèi)存空間,,競(jìng)爭(zhēng)同一個(gè)鎖的概率非常小,,分段鎖反而會(huì)造成效率低。

7. 寫一段代碼在遍歷 ArrayList 時(shí)移除一個(gè)元素

因?yàn)閒oreach刪除會(huì)導(dǎo)致快速失敗問(wèn)題,,fori順序遍歷會(huì)導(dǎo)致重復(fù)元素沒刪除,,所以正確解法如下:

第一種遍歷,倒敘遍歷刪除

for(int i=list.size()-1; i>-1; i--){ if(list.get(i).equals("jay")){ list.remove(list.get(i)); }}

第二種,,迭代器刪除

Iterator itr = list.iterator();while(itr.hasNext()) { if(itr.next().equals("jay") { itr.remove(); }}

8. Java中怎么打印數(shù)組,?

數(shù)組是不能直接打印的哈,如下:

public class Test {
public static void main(String[] args) { String[] jayArray = {"jay", "boy"}; System.out.println(jayArray); }}//output[Ljava.lang.String;@1540e19d

打印數(shù)組可以用流的方式Strem.of().foreach(),,如下:

public class Test {
public static void main(String[] args) { String[] jayArray = {"jay", "boy"}; Stream.of(jayArray).forEach(System.out::println); }}//outputjayboy

打印數(shù)組,,最優(yōu)雅的方式可以用這個(gè)APi,Arrays.toString()

public class Test { public static void main(String[] args) { String[] jayArray = {"jay", "boy"}; System.out.println(Arrays.toString(jayArray)); }}//output[jay, boy]

9. TreeMap底層?

  • TreeMap實(shí)現(xiàn)了SotredMap接口,,它是有序的集合,。
  • TreeMap底層數(shù)據(jù)結(jié)構(gòu)是一個(gè)紅黑樹,每個(gè)key-value都作為一個(gè)紅黑樹的節(jié)點(diǎn),。
  • 如果在調(diào)用TreeMap的構(gòu)造函數(shù)時(shí)沒有指定比較器,,則根據(jù)key執(zhí)行自然排序。

10. HashMap 的擴(kuò)容過(guò)程

Hashmap的擴(kuò)容:

  • 第一步把數(shù)組長(zhǎng)度變?yōu)樵瓉?lái)的兩倍,,
  • 第二步把舊數(shù)組的元素重新計(jì)算hash插入到新數(shù)組中,。
  • jdk8時(shí),不用重新計(jì)算hash,,只用看看原來(lái)的hash值新增的一位是零還是1,如果是1這個(gè)元素在新數(shù)組中的位置,,是原數(shù)組的位置加原數(shù)組長(zhǎng)度,,如果是零就插入到原數(shù)組中。擴(kuò)容過(guò)程第二步一個(gè)非常重要的方法是transfer方法,,采用頭插法,,把舊數(shù)組的元素插入到新數(shù)組中。

11. HashSet是如何保證不重復(fù)的

可以看一下HashSet的add方法,,元素E作為HashMap的key,,我們都知道HashMap的可以是不允許重復(fù)的,哈哈。

public boolean add(E e) { return map.put(e, PRESENT)==null;}

12. HashMap 是線程安全的嗎,,為什么不是線程安全的,?死循環(huán)問(wèn)題?

不是線性安全的,。

并發(fā)的情況下,,擴(kuò)容可能導(dǎo)致死循環(huán)問(wèn)題。

13. LinkedHashMap的應(yīng)用,,底層,,原理

  • LinkedHashMap維護(hù)著一個(gè)運(yùn)行于所有條目的雙重鏈接列表。此鏈接列表定義了迭代順序,,該迭代順序可以是插入順序(insert-order)或者是訪問(wèn)順序,,其中默認(rèn)的迭代訪問(wèn)順序就是插入順序,即可以按插入的順序遍歷元素,,這點(diǎn)和HashMap有很大的不同,。
  • LRU算法可以用LinkedHashMap實(shí)現(xiàn)。

14. 哪些集合類是線程安全的,?哪些不安全,?

線性安全的

  • Vector:比Arraylist多了個(gè)同步化機(jī)制。
  • Hashtable:比Hashmap多了個(gè)線程安全,。
  • ConcurrentHashMap:是一種高效但是線程安全的集合,。
  • Stack:棧,也是線程安全的,,繼承于Vector,。

線性不安全的

  • Hashmap
  • Arraylist
  • LinkedList
  • HashSet
  • TreeSet
  • TreeMap

15. ArrayList 和 Vector 的區(qū)別是什么?

  • Vector是線程安全的,,ArrayList不是線程安全的,。
  • ArrayList在底層數(shù)組不夠用時(shí)在原來(lái)的基礎(chǔ)上擴(kuò)展0.5倍,Vector是擴(kuò)展1倍,。
  • Vector只要是關(guān)鍵性的操作,,方法前面都加了synchronized關(guān)鍵字,來(lái)保證線程的安全性,。

16. Collection與Collections的區(qū)別是什么,?

  • Collection是Java集合框架中的基本接口,如List接口也是繼承于它
public interface List<E> extends Collection<E> {
  • Collections是Java集合框架提供的一個(gè)工具類,,其中包含了大量用于操作或返回集合的靜態(tài)方法,。如下:
public static <T extends Comparable<? super T>> void sort(List<T> list) { list.sort(null);}

17. 如何決定使用 HashMap 還是TreeMap?

這個(gè)點(diǎn),,主要考察HashMap和TreeMap的區(qū)別,。

TreeMap實(shí)現(xiàn)SortMap接口,,能夠把它保存的記錄根據(jù)鍵排序,默認(rèn)是按key的升序排序,,也可以指定排序的比較器,。當(dāng)用Iterator遍歷TreeMap時(shí),得到的記錄是排過(guò)序的,。

18. 如何實(shí)現(xiàn)數(shù)組和 List之間的轉(zhuǎn)換,?

List 轉(zhuǎn) Array

List 轉(zhuǎn)Array,必須使用集合的 toArray(T[] array),,如下:

List<String> list = new ArrayList<String>();list.add("jay");list.add("tianluo");
// 使用泛型,,無(wú)需顯式類型轉(zhuǎn)換String[] array = list.toArray(new String[list.size()]);System.out.println(array[0]);

如果直接使用 toArray 無(wú)參方法,返回值只能是 Object[] 類,,強(qiáng)轉(zhuǎn)其他類型可能有問(wèn)題,,demo如下:

List<String> list = new ArrayList<String>();list.add("jay");list.add("tianluo");
String[] array = (String[]) list.toArray();System.out.println(array[0]);

運(yùn)行結(jié)果:

Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String; at Test.main(Test.java:14)

Array 轉(zhuǎn)List

使用Arrays.asList() 把數(shù)組轉(zhuǎn)換成集合時(shí),不能使用修改集合相關(guān)的方法啦,,如下:

String[] str = new String[] { "jay", "tianluo" };List list = Arrays.asList(str);list.add("boy");

運(yùn)行結(jié)果如下:

Exception in thread "main" java.lang.UnsupportedOperationException at java.util.AbstractList.add(AbstractList.java:148) at java.util.AbstractList.add(AbstractList.java:108) at Test.main(Test.java:13)

因?yàn)?Arrays.asList不是返回java.util.ArrayList,而是一個(gè)內(nèi)部類ArrayList,。

可以這樣使用彌補(bǔ)這個(gè)缺點(diǎn):

//方式一:ArrayList< String> arrayList = new ArrayList<String>(strArray.length);Collections.addAll(arrayList, strArray);//方式二:ArrayList<String> list = new ArrayList<String>(Arrays.asList(strArray)) ;

19. 迭代器 Iterator 是什么?怎么用,,有什么特點(diǎn),?

public interface Collection<E> extends Iterable<E> {
Iterator<E> iterator();

方法如下:

next() 方法獲得集合中的下一個(gè)元素hasNext() 檢查集合中是否還有元素remove() 方法將迭代器新返回的元素刪除forEachRemaining(Consumer<? super E> action) 方法,遍歷所有元素

Iterator 主要是用來(lái)遍歷集合用的,,它的特點(diǎn)是更加安全,,因?yàn)樗梢源_保,在當(dāng)前遍歷的集合元素被更改的時(shí)候,,就會(huì)拋出 ConcurrentModificationException 異常,。

使用demo如下:

List<String> list = new ArrayList<>();Iterator<String> it = list. iterator();while(it. hasNext()){ String obj = it. next(); System. out. println(obj);}

20. Iterator 和 ListIterator 有什么區(qū)別?

  • ListIterator 比 Iterator有更多的方法,。
  • ListIterator只能用于遍歷List及其子類,,Iterator可用來(lái)遍歷所有集合,
  • ListIterator遍歷可以是逆向的,,因?yàn)橛衟revious()和hasPrevious()方法,,而Iterator不可以。
  • ListIterator有add()方法,,可以向List添加對(duì)象,,而Iterator卻不能。
  • ListIterator可以定位當(dāng)前的索引位置,,因?yàn)橛衝extIndex()和previousIndex()方法,而Iterator不可以,。
  • ListIterator可以實(shí)現(xiàn)對(duì)象的修改,,set()方法可以實(shí)現(xiàn),。Iierator僅能遍歷,不能修改哦,。

21. 怎么確保一個(gè)集合不能被修改,?

很多朋友很可能想到用final關(guān)鍵字進(jìn)行修飾,final修飾的這個(gè)成員變量,,如果是基本數(shù)據(jù)類型,,表示這個(gè)變量的值是不可改變的,如果是引用類型,,則表示這個(gè)引用的地址值是不能改變的,,但是這個(gè)引用所指向的對(duì)象里面的內(nèi)容還是可以改變滴~驗(yàn)證一下,如下:

public class Test { //final 修飾 private static final Map<Integer, String> map = new HashMap<Integer, String>(); { map.put(1, "jay"); map.put(2, "tianluo"); }
public static void main(String[] args) { map.put(1, "boy"); System.out.println(map.get(1)); }}

運(yùn)行結(jié)果如下:

//可以洗發(fā)現(xiàn),,final修飾,,集合還是會(huì)被修改呢boy

嘻嘻,那么,,到底怎么確保一個(gè)集合不能被修改呢,,看以下這三哥們~

  • unmodifiableMap
  • unmodifiableList
  • unmodifiableSet

再看一下demo吧

public class Test {
private static Map<Integer, String> map = new HashMap<Integer, String>(); { map.put(1, "jay"); map.put(2, "tianluo");
}
public static void main(String[] args) { map = Collections.unmodifiableMap(map); map.put(1, "boy"); System.out.println(map.get(1)); }}

運(yùn)行結(jié)果:

// 可以發(fā)現(xiàn),,unmodifiableMap確保集合不能修改啦,,拋異常了Exception in thread "main" java.lang.UnsupportedOperationException at java.util.Collections$UnmodifiableMap.put(Collections.java:1457) at Test.main(Test.java:14)

22. 快速失敗(fail-fast)和安全失敗(fail-safe)的區(qū)別是什么?

快速失敗

在用迭代器遍歷一個(gè)集合對(duì)象時(shí),,如果遍歷過(guò)程中對(duì)集合對(duì)象的內(nèi)容進(jìn)行了修改(增加,、刪除,、修改),則會(huì)拋出Concurrent Modification Exception,。

public class Test {
public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(1); list.add(2);
Iterator iterator = list.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); list.add(3); System.out.println(list.size()); }
}}

運(yùn)行結(jié)果:

1Exception in thread "main" java.util.ConcurrentModificationException3 at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909) at java.util.ArrayList$Itr.next(ArrayList.java:859) at Test.main(Test.java:12)

安全失敗

采用安全失敗機(jī)制的集合容器,,在遍歷時(shí)不是直接在集合內(nèi)容上訪問(wèn)的,而是先復(fù)制原有集合內(nèi)容,,在拷貝的集合上進(jìn)行遍歷,。

public class Test {
public static void main(String[] args) { List<Integer> list = new CopyOnWriteArrayList<>(); list.add(1); list.add(2);
Iterator iterator = list.iterator(); while (iterator.hasNext()) { System.out.println(iterator.next()); list.add(3); System.out.println("list size:"+list.size()); }
}}

運(yùn)行結(jié)果:

1list size:32list size:4

其實(shí),在java.util.concurrent 并發(fā)包的集合,,如 ConcurrentHashMap, CopyOnWriteArrayList等,,默認(rèn)為都是安全失敗的。

23. 什么是Java優(yōu)先級(jí)隊(duì)列(Priority Queue),?

優(yōu)先隊(duì)列PriorityQueue是Queue接口的實(shí)現(xiàn),,可以對(duì)其中元素進(jìn)行排序

  • 優(yōu)先隊(duì)列中元素默認(rèn)排列順序是升序排列
  • 但對(duì)于自己定義的類來(lái)說(shuō),需要自己定義比較器
public class PriorityQueue<E> extends AbstractQueue<E> implements java.io.Serializable { ... private final Comparator<? super E> comparator;

方法:

peek()//返回隊(duì)首元素poll()//返回隊(duì)首元素,,隊(duì)首元素出隊(duì)列add()//添加元素size()//返回隊(duì)列元素個(gè)數(shù)isEmpty()//判斷隊(duì)列是否為空,,為空返回true,不空返回false

特點(diǎn):

  • 1.基于優(yōu)先級(jí)堆
  • 2.不允許null值
  • 3.線程不安全
  • 4.出入隊(duì)時(shí)間復(fù)雜度O(log(n))
  • 5.調(diào)用remove()返回堆內(nèi)最小值

24. JAVA8的ConcurrentHashMap為什么放棄了分段鎖,有什么問(wèn)題嗎,,如果你來(lái)設(shè)計(jì),,你如何設(shè)計(jì),。

jdk8 放棄了分段鎖而是用了Node鎖,減低鎖的粒度,,提高性能,,并使用CAS操作來(lái)確保Node的一些操作的原子性,取代了鎖,。

可以跟面試官聊聊悲觀鎖和CAS樂(lè)觀鎖的區(qū)別,,優(yōu)缺點(diǎn)哈~

25. 阻塞隊(duì)列的實(shí)現(xiàn),ArrayBlockingQueue的底層實(shí)現(xiàn),?

ArrayBlockingQueue是數(shù)組實(shí)現(xiàn)的線程安全的有界的阻塞隊(duì)列,,繼承自AbstractBlockingQueue,間接的實(shí)現(xiàn)了Queue接口和Collection接口。底層以數(shù)組的形式保存數(shù)據(jù)(實(shí)際上可看作一個(gè)循環(huán)數(shù)組),。常用的操作包括 add ,offer,put,,remove,poll,take,peek。

可以結(jié)合線程池跟面試官講一下哦~

26. Java 中的 LinkedList是單向鏈表還是雙向鏈表,?

哈哈,,看源碼吧,是雙向鏈表

private static class Node<E> { E item; Node<E> next; Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) { this.item = element; this.next = next; this.prev = prev; } }

27. 說(shuō)一說(shuō)ArrayList 的擴(kuò)容機(jī)制吧

ArrayList擴(kuò)容的本質(zhì)就是計(jì)算出新的擴(kuò)容數(shù)組的size后實(shí)例化,,并將原有數(shù)組內(nèi)容復(fù)制到新數(shù)組中去,。


public boolean add(E e) { //擴(kuò)容 ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));}
private static int calculateCapacity(Object[] elementData, int minCapacity) { //如果傳入的是個(gè)空數(shù)組則最小容量取默認(rèn)容量與minCapacity之間的最大值 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; } private void ensureExplicitCapacity(int minCapacity) { modCount++; // 如果最小需要空間比elementData的內(nèi)存空間要大,,則需要擴(kuò)容 // overflow-conscious code if (minCapacity - elementData.length > 0) grow(minCapacity); } private void grow(int minCapacity) { // 獲取elementData數(shù)組的內(nèi)存空間長(zhǎng)度 int oldCapacity = elementData.length; // 擴(kuò)容至原來(lái)的1.5倍 int newCapacity = oldCapacity + (oldCapacity >> 1); //校驗(yàn)容量是否夠 if (newCapacity - minCapacity < 0) newCapacity = minCapacity; //若預(yù)設(shè)值大于默認(rèn)的最大值,,檢查是否溢出 if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // 調(diào)用Arrays.copyOf方法將elementData數(shù)組指向新的內(nèi)存空間 //并將elementData的數(shù)據(jù)復(fù)制到新的內(nèi)存空間 elementData = Arrays.copyOf(elementData, newCapacity); }

28. HashMap 的長(zhǎng)度為什么是2的冪次方,以及其他常量定義的含義~

為了能讓HashMap存取高效,,數(shù)據(jù)分配均勻,。

看著呢,以下等式相等,,但是位移運(yùn)算比取余效率高很多呢~

hash%length=hash&(length-1)

可以看下我這篇文章哈~面試加分項(xiàng)-HashMap源碼中這些常量的設(shè)計(jì)目的

29. ConcurrenHashMap 原理,?1.8 中為什么要用紅黑樹?

聊到ConcurrenHashMap,,需要跟面試官聊到安全性,,分段鎖segment,為什么放棄了分段鎖,,與及選擇CAS,,其實(shí)就是都是從效率和安全性觸發(fā),嘻嘻~

java8不是用紅黑樹來(lái)管理hashmap,而是在hash值相同的情況下(且重復(fù)數(shù)量大于8),用紅黑樹來(lái)管理數(shù)據(jù),。紅黑樹相當(dāng)于排序數(shù)據(jù),。可以自動(dòng)的使用二分法進(jìn)行定位,。性能較高,。

30. ArrayList的默認(rèn)大小

ArrayList 的默認(rèn)大小是 10 個(gè)元素

/** * Default initial capacity. */private static final int DEFAULT_CAPACITY = 10;

31. 為何Collection不從Cloneable和Serializable接口繼承,?

  • Collection表示一個(gè)集合,包含了一組對(duì)象元素,。如何維護(hù)它的元素對(duì)象是由具體實(shí)現(xiàn)來(lái)決定的。因?yàn)榧系木唧w形式多種多樣,,例如list允許重復(fù),,set則不允許。而克?。╟lone)和序列化(serializable)只對(duì)于具體的實(shí)體,,對(duì)象有意義,你不能說(shuō)去把一個(gè)接口,,抽象類克隆,,序列化甚至反序列化。所以具體的collection實(shí)現(xiàn)類是否可以克隆,,是否可以序列化應(yīng)該由其自身決定,,而不能由其超類強(qiáng)行賦予。
  • 如果collection繼承了clone和serializable,,那么所有的集合實(shí)現(xiàn)都會(huì)實(shí)現(xiàn)這兩個(gè)接口,,而如果某個(gè)實(shí)現(xiàn)它不需要被克隆,甚至不允許它序列化(序列化有風(fēng)險(xiǎn)),,那么就與collection矛盾了,。

32. Enumeration和Iterator接口的區(qū)別?

public interface Enumeration<E> { boolean hasMoreElements(); E nextElement();}public interface Iterator<E> { boolean hasNext(); E next(); void remove();}
  • 函數(shù)接口不同
  • Enumeration速度快,,占用內(nèi)存少,,但是不是快速失敗的,線程不安全,。
  • Iterator允許刪除底層數(shù)據(jù),,枚舉不允許
  • Iterator安全性高,因?yàn)槠渌€程不能夠修改正在被Iterator遍歷的集合里面的對(duì)象,。

33. 我們?nèi)绾螌?duì)一組對(duì)象進(jìn)行排序,?

可以用 Collections.sort()+ Comparator.comparing(),因?yàn)閷?duì)對(duì)象排序,,實(shí)際上是對(duì)對(duì)象的屬性排序哈~

public class Student {
private String name; private int score;
public Student(String name, int score){ this.name = name; this.score = score; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getScore() { return score; }
public void setScore(int score) { this.score = score; }
@Override public String toString() { return "Student: " + this.name + " 分?jǐn)?shù):" + Integer.toString( this.score ); }}
public class Test {
public static void main(String[] args) {
List<Student> studentList = new ArrayList<>(); studentList.add(new Student("D", 90)); studentList.add(new Student("C", 100)); studentList.add(new Student("B", 95)); studentList.add(new Student("A", 95));
Collections.sort(studentList, Comparator.comparing(Student::getScore).reversed().thenComparing(Student::getName)); studentList.stream().forEach(p -> System.out.println(p.toString())); }}

34. 當(dāng)一個(gè)集合被作為參數(shù)傳遞給一個(gè)函數(shù)時(shí),,如何才可以確保函數(shù)不能修改它?

這個(gè)跟之前那個(gè)不可變集合一樣道理哈~

在作為參數(shù)傳遞之前,,使用Collections.unmodifiableCollection(Collection c)方法創(chuàng)建一個(gè)只讀集合,,這將確保改變集合的任何操作都會(huì)拋出UnsupportedOperationException。

35. 說(shuō)一下HashSet的實(shí)現(xiàn)原理,?

  • 不能保證元素的排列順序,,順序有可能發(fā)生變化,。
  • 元素可以為null
  • hashset保證元素不重復(fù)~ (這個(gè)面試官很可能會(huì)問(wèn)什么原理,這個(gè)跟HashMap有關(guān)的哦)
  • HashSet,,需要談?wù)勊鼈zhashcode()和equles()哦~
  • 實(shí)際是基于HashMap實(shí)現(xiàn)的,,HashSet 底層使用HashMap來(lái)保存所有元素的

看看它的add方法吧~

public boolean add(E e) { return map.put(e, PRESENT)==null; }

36. Array 和 ArrayList 有何區(qū)別?

  • 定義一個(gè) Array 時(shí),,必須指定數(shù)組的數(shù)據(jù)類型及數(shù)組長(zhǎng)度,,即數(shù)組中存放的元素個(gè)數(shù)固定并且類型相同。
  • ArrayList 是動(dòng)態(tài)數(shù)組,,長(zhǎng)度動(dòng)態(tài)可變,,會(huì)自動(dòng)擴(kuò)容。不使用泛型的時(shí)候,,可以添加不同類型元素,。

37. 為什么HashMap中String、Integer這樣的包裝類適合作為key,?

String,、Integer等包裝類的特性能夠保證Hash值的不可更改性和計(jì)算準(zhǔn)確性,能夠有效的減少Hash碰撞的幾率~

因?yàn)?/p>

  • 它們都是final修飾的類,,不可變性,,保證key的不可更改性,不會(huì)存在獲取hash值不同的情況~
  • 它們內(nèi)部已重寫了equals(),、hashCode()等方法,,遵守了HashMap內(nèi)部的規(guī)范

38. 如果想用Object作為hashMap的Key?,;

重寫hashCode()和equals()方法啦~ (這個(gè)答案來(lái)自互聯(lián)網(wǎng)哈~)

  • 重寫hashCode()是因?yàn)樾枰?jì)算存儲(chǔ)數(shù)據(jù)的存儲(chǔ)位置,,需要注意不要試圖從散列碼計(jì)算中排除掉一個(gè)對(duì)象的關(guān)鍵部分來(lái)提高性能,這樣雖然能更快但可能會(huì)導(dǎo)致更多的Hash碰撞,;
  • 重寫equals()方法,,需要遵守自反性、對(duì)稱性,、傳遞性,、一致性以及對(duì)于任何非null的引用值x,x.equals(null)必須返回false的這幾個(gè)特性,,目的是為了保證key在哈希表中的唯一性,;

39. 講講紅黑樹的特點(diǎn)?

  • 每個(gè)節(jié)點(diǎn)或者是黑色,,或者是紅色,。
  • 根節(jié)點(diǎn)是黑色。
  • 每個(gè)葉子節(jié)點(diǎn)(NIL)是黑色。[注意:這里葉子節(jié)點(diǎn),,是指為空(NIL或NULL)的葉子節(jié)點(diǎn),!]
  • 如果一個(gè)節(jié)點(diǎn)是紅色的,則它的子節(jié)點(diǎn)必須是黑色的,。
  • 從一個(gè)節(jié)點(diǎn)到該節(jié)點(diǎn)的子孫節(jié)點(diǎn)的所有路徑上包含相同數(shù)目的黑節(jié)點(diǎn),。

40. Java集合類框架的最佳實(shí)踐有哪些?

其實(shí)這些點(diǎn),,結(jié)合平時(shí)工作,,代碼總結(jié)講出來(lái),更容易吸引到面試官呢 (這個(gè)答案來(lái)自互聯(lián)網(wǎng)哈~)

  • 1.根據(jù)應(yīng)用需要正確選擇要使用的集合類型對(duì)性能非常重要,,比如:假如知道元素的大小是固定的,那么選用Array類型而不是ArrayList類型更為合適,。
  • 2.有些集合類型允許指定初始容量,。因此,如果我們能估計(jì)出存儲(chǔ)的元素的數(shù)目,,我們可以指定初始容量來(lái)避免重新計(jì)算hash值或者擴(kuò)容等,。
  • 3.為了類型安全、可讀性和健壯性等原因總是要使用泛型,。同時(shí),,使用泛型還可以避免運(yùn)行時(shí)的ClassCastException。
  • 4.使用JDK提供的不變類(immutable class)作為Map的鍵可以避免為我們自己的類實(shí)現(xiàn)hashCode()和equals()方法,。
  • 5.編程的時(shí)候接口優(yōu)于實(shí)現(xiàn)
  • 6.底層的集合實(shí)際上是空的情況下,,返回為長(zhǎng)度是0的集合或數(shù)組而不是null。

41.談?wù)劸€程池阻塞隊(duì)列吧~

  • ArrayBlockingQueue
  • LinkedBlockingQueue
  • DelayQueue
  • PriorityBlockingQueue
  • SynchronousQueue

ArrayBlockingQueue: (有界隊(duì)列)是一個(gè)用數(shù)組實(shí)現(xiàn)的有界阻塞隊(duì)列,,按FIFO排序量,。

LinkedBlockingQueue: (可設(shè)置容量隊(duì)列)基于鏈表結(jié)構(gòu)的阻塞隊(duì)列,按FIFO排序任務(wù),,容量可以選擇進(jìn)行設(shè)置,,不設(shè)置的話,將是一個(gè)無(wú)邊界的阻塞隊(duì)列,,最大長(zhǎng)度為Integer.MAX_VALUE,,吞吐量通常要高于ArrayBlockingQuene;newFixedThreadPool線程池使用了這個(gè)隊(duì)列

DelayQueue:(延遲隊(duì)列)是一個(gè)任務(wù)定時(shí)周期的延遲執(zhí)行的隊(duì)列,。根據(jù)指定的執(zhí)行時(shí)間從小到大排序,,否則根據(jù)插入到隊(duì)列的先后排序。newScheduledThreadPool線程池使用了這個(gè)隊(duì)列,。

PriorityBlockingQueue:(優(yōu)先級(jí)隊(duì)列)是具有優(yōu)先級(jí)的無(wú)界阻塞隊(duì)列,;

SynchronousQueue:(同步隊(duì)列)一個(gè)不存儲(chǔ)元素的阻塞隊(duì)列,每個(gè)插入操作必須等到另一個(gè)線程調(diào)用移除操作,否則插入操作一直處于阻塞狀態(tài),,吞吐量通常要高于LinkedBlockingQuene,,newCachedThreadPool線程池使用了這個(gè)隊(duì)列。針對(duì)面試題:線程池都有哪幾種工作隊(duì)列,?

我覺得,,回答以上幾種ArrayBlockingQueue,LinkedBlockingQueue,,SynchronousQueue等,,說(shuō)出它們的特點(diǎn),并結(jié)合使用到對(duì)應(yīng)隊(duì)列的常用線程池(如newFixedThreadPool線程池使用LinkedBlockingQueue),,進(jìn)行展開闡述,, 就可以啦。

有興趣的朋友,,可以看看我的這篇文章哦~

面試必備:Java線程池解析

42. HashSet和TreeSet有什么區(qū)別,?

  • Hashset 的底層是由哈希表實(shí)現(xiàn)的,Treeset 底層是由紅黑樹實(shí)現(xiàn)的,。
  • HashSet中的元素沒有順序,,TreeSet保存的元素有順序性(實(shí)現(xiàn)Comparable接口)
  • HashSet的add(),remove(),,contains()方法的時(shí)間復(fù)雜度是O(1);TreeSet中,,add(),remove(),,contains()方法的時(shí)間復(fù)雜度是O(logn)

43. Set里的元素是不能重復(fù)的,,那么用什么方法來(lái)區(qū)分重復(fù)與否呢? 是用==還是equals()?

元素重復(fù)與否是使用equals()方法進(jìn)行判斷的,這個(gè)可以跟面試官說(shuō)說(shuō)==和equals()的區(qū)別,,hashcode()和equals

44. 說(shuō)出ArrayList,LinkedList的存儲(chǔ)性能和特性

這道面試題,,跟ArrayList,LinkedList,就是換湯不換藥的~

  • ArrayList,使用數(shù)組方式存儲(chǔ)數(shù)據(jù),,查詢時(shí),,ArrayList是基于索引(index)的數(shù)據(jù)結(jié)構(gòu),可以直接映射到,,速度較快,;但是插入數(shù)據(jù)需要移動(dòng)數(shù)據(jù),效率就比LinkedList慢一點(diǎn)~
  • LinkedList,使用雙向鏈表實(shí)現(xiàn)存儲(chǔ),按索引數(shù)據(jù)需要進(jìn)行前向或后向遍歷,,查詢相對(duì)ArrayList慢一點(diǎn),;但是插入數(shù)據(jù)速度較快。
  • LinkedList比ArrayList開銷更大,,因?yàn)長(zhǎng)inkedList的節(jié)點(diǎn)除了存儲(chǔ)數(shù)據(jù),,還需要存儲(chǔ)引用。

45. HashMap在JDK1.7和JDK1.8中有哪些不同?

互聯(lián)網(wǎng)上這個(gè)答案太詳細(xì)啦(https://www.jianshu.com/p/939b8a672070)

46. ArrayList集合加入1萬(wàn)條數(shù)據(jù),,應(yīng)該怎么提高效率

因?yàn)锳rrayList的底層是數(shù)組實(shí)現(xiàn),并且數(shù)組的默認(rèn)值是10,如果插入10000條要不斷的擴(kuò)容,耗費(fèi)時(shí)間,所以我們調(diào)用ArrayList的指定容量的構(gòu)造器方法ArrayList(int size) 就可以實(shí)現(xiàn)不擴(kuò)容,就提高了性能,。

47. 如何對(duì)Object的list排序

看例子吧,哈哈,,這個(gè)跟對(duì)象排序也是一樣的呢~

public class Person {
private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Person(String name, Integer age) { this.name = name; this.age = age; }}
public class Test {
public static void main(String[] args) {
List<Person> list = new ArrayList<>(); list.add(new Person("jay", 18)); list.add(new Person("tianLuo", 10));
list.stream().forEach(p -> System.out.println(p.getName()+" "+p.getAge())); // 用comparing比較對(duì)象屬性 list.sort(Comparator.comparing(Person::getAge));
System.out.println("排序后");
list.stream().forEach(p -> System.out.print(p.getName()+" "+p.getAge()+" ")); }}

48. ArrayList 和 HashMap 的默認(rèn)大小是多數(shù),?

在 Java 7 中,ArrayList 的默認(rèn)大小是 10 個(gè)元素,,HashMap 的默認(rèn)大小是16個(gè)元素(必須是2的冪),。

49. 有沒有有順序的Map實(shí)現(xiàn)類,如果有,,他們是怎么保證有序的

  • Hashmap和Hashtable 都不是有序的,。
  • TreeMap和LinkedHashmap都是有序的。(TreeMap默認(rèn)是key升序,,LinkedHashmap默認(rèn)是數(shù)據(jù)插入順序)
  • TreeMap是基于比較器Comparator來(lái)實(shí)現(xiàn)有序的,。
  • LinkedHashmap是基于鏈表來(lái)實(shí)現(xiàn)數(shù)據(jù)插入有序的。

50. HashMap是怎么解決哈希沖突的

Hashmap解決hash沖突,,使用的是鏈地址法,即數(shù)組+鏈表的形式來(lái)解決,。put執(zhí)行首先判斷table[i]位置,,如果為空就直接插入,不為空判斷和當(dāng)前值是否相等,,相等就覆蓋,,如果不相等的話,判斷是否是紅黑樹節(jié)點(diǎn),,如果不是,,就從table[i]位置開始遍歷鏈表,相等覆蓋,,不相等插入,。

個(gè)人公眾號(hào)

  • 如果你是個(gè)愛學(xué)習(xí)的好孩子,可以關(guān)注我公眾號(hào),,一起學(xué)習(xí)討論,。
  • 如果你覺得本文有哪些不正確的地方,可以評(píng)論,,也可以關(guān)注我公眾號(hào),,私聊我,大家一起學(xué)習(xí)進(jìn)步哈,。

    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多