前言來(lái)了來(lái)了,,50道Java集合面試題也來(lái)啦~ 已經(jīng)上傳github:
1. Arraylist與LinkedList區(qū)別可以從它們的底層數(shù)據(jù)結(jié)構(gòu),、效率、開銷進(jìn)行闡述哈
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方法底層就是:
Timesort排序Timsort排序是結(jié)合了合并排序(merge.sort)和插入排序(insertion sort)而得出的排序方法,; 1.當(dāng)數(shù)組長(zhǎng)度小于某個(gè)值,,采用的是二分插入排序算法,如下:
3. HashMap原理,,java8做了什么改變
有關(guān)于HashMap這些常量設(shè)計(jì)目的,,也可以看我這篇文章:面試加分項(xiàng)-HashMap源碼中這些常量的設(shè)計(jì)目的 4. List 和 Set,Map 的區(qū)別
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 */ E 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
HashTable
ConcurrentHashMap
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); } } //output jay boy 打印數(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底層?
10. HashMap 的擴(kuò)容過(guò)程Hashmap的擴(kuò)容:
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)用,,底層,,原理
14. 哪些集合類是線程安全的,?哪些不安全,?線性安全的
線性不安全的
15. ArrayList 和 Vector 的區(qū)別是什么?
16. Collection與Collections的區(qū)別是什么,?
public interface List<E> extends Collection<E> {
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) ArrayList 轉(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ū)別?
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è)集合不能被修改呢,,看以下這三哥們~
再看一下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é)果: 1 Exception in thread "main" java.util.ConcurrentModificationException 3 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é)果: 1 list size:3 2 list 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)行排序
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):
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接口繼承,?
32. Enumeration和Iterator接口的區(qū)別?public interface Enumeration<E> { boolean hasMoreElements(); E nextElement(); } public interface Iterator<E> { boolean hasNext(); E next(); void remove(); }
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è)不可變集合一樣道理哈~
35. 說(shuō)一下HashSet的實(shí)現(xiàn)原理,?
看看它的add方法吧~ public boolean add(E e) { return map.put(e, PRESENT)==null; } 36. Array 和 ArrayList 有何區(qū)別?
37. 為什么HashMap中String、Integer這樣的包裝類適合作為key,?String,、Integer等包裝類的特性能夠保證Hash值的不可更改性和計(jì)算準(zhǔn)確性,能夠有效的減少Hash碰撞的幾率~ 因?yàn)?/p>
38. 如果想用Object作為hashMap的Key?,;重寫hashCode()和equals()方法啦~ (這個(gè)答案來(lái)自互聯(lián)網(wǎng)哈~)
39. 講講紅黑樹的特點(diǎn)?
40. Java集合類框架的最佳實(shí)踐有哪些?其實(shí)這些點(diǎn),,結(jié)合平時(shí)工作,,代碼總結(jié)講出來(lái),更容易吸引到面試官呢 (這個(gè)答案來(lái)自互聯(lián)網(wǎng)哈~)
41.談?wù)劸€程池阻塞隊(duì)列吧~
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)行展開闡述,, 就可以啦。 有興趣的朋友,,可以看看我的這篇文章哦~ 42. HashSet和TreeSet有什么區(qū)別,?
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,就是換湯不換藥的~
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)該怎么提高效率
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)類,如果有,,他們是怎么保證有序的
50. HashMap是怎么解決哈希沖突的
個(gè)人公眾號(hào)
|
|