二、容器

☞ java容器都有哪些?

常用容器:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-12Qc8R6r-1571392817152)(https://note.youdao.com/yws/res/7645/8233303C35754917A1E197FBAF8EF788)]

☞ Collection和Collections有什麼區別?

  • java.util.Collection 是一個集合接口(集合類的一個頂級接口)。它提供了對集合對象進行基本操作的通用接口方法。Collection接口在Java 類庫中有很多具體的實現。
  • Collection接口的意義是爲各種具體的集合提供了最大化的統一操作方式,其直接繼承接口有List與Set。
    Collections則是集合類的一個工具類/幫助類,其中提供了一系列靜態方法,用於對集合中元素進行排序、搜索以及線程安全等各種操作。

☞ List、Set、Map之間的區別是什麼?

List Set Map
繼承接口 Collection Collection
實現類 AbstractList(ArrayList,Vector,LinkedList) AbstractSet(HashSet,LinkedHashSet,TreeSet) HashMap,HashTable
常用方法 add(),remove(),clear(),get(),contains(),size() add(),remove(),clear(),contains(),size() get(),put(),remove(),clear(),containsKey(),cotainsValue(),size(),keySet(),values()
是否有序 有序 無序(實際上由hashCode決定)
元素重複性 可重複 不可重複 不可重複
線程安全 Vector線程安全 HashTable線程安全

☞ HashMap和Hashtable有什麼區別?

  • HashMap沒有HashTable的contains()方法,有containsKey()和containsValue()方法
  • HashMap線程不安全,HashTable線程安全;HashMap效率比HashTable高
  • HashMap允許鍵值爲空;HashTable不允許
  • HashTable的線程安全是通過synchronized實現的

多線程下使用HashMap,如何保證線程安全?

使用Collections.synchronizedMap(m)獲取一個被裝飾過的map,該map的所有方法被重寫,均加了synchronized修飾,源碼示例如下

private static class SynchronizedMap<K, V> implements Map<K, V>, Serializable {
	private static final long serialVersionUID = 1978198479659022715L;

	private final Map<K, V> m; // Backing Map
	final Object mutex; // Object on which to synchronize
	SynchronizedMap(Map<K, V> m) {
		this.m = Objects.requireNonNull(m);
		mutex = this;
	}
	SynchronizedMap(Map<K, V> m, Object mutex) {
		this.m = m;
		this.mutex = mutex;
	}
	public int size() {
		synchronized (mutex) {
			return m.size();
		}
	}
	public boolean isEmpty() {
		synchronized (mutex) {
			return m.isEmpty();
		}
	}
	...
}

SynchronizedMap是Collections的內部類,實現了Map接口,故此處使用的裝飾者模式(即靜態代理)

☞ 如何決定使用HashMap還是TreeMap?

如果只是對元素進行插入,刪除,定位元素這類操作,HashMap是最好的選擇;如果需要給元素排序,就是用TreeMap

☞ 說一下HashMap的實現原理?

Java中數組查詢快增刪慢,鏈表增刪快但是查詢慢;hashmap結合數組和鏈表的優點,使得查詢和增刪都非常快,時間複雜度平均爲O(1);hashmap底層是一個可以擴展的entry數組,用於存儲鍵值對;在添加元素時,hashmap會重新計算key的hash值,從而得到元素在entry數組中的下標;如果數組在該位置已經存在元素了,則該位置的元素以鏈表的形式存在,新增加的元素放置在鏈表的首部;在jdk1.8中,如果鏈表中的節點超過8個後,鏈表會轉化爲紅黑樹,以提高查詢效率,時間複雜度平均由O(n)降低至O(log(n))

☞ 說一下HashSet的實現原理?

  • HashSet底層由HashMap實現
  • HashSet的值存放於HashMap的key上
  • HashSet無序,允許null值存入,非線程安全

☞ ArrayList和LinkedList的區別是什麼?

  1. 兩者都是線程不安全的容器
  2. ArrayList底層使用數組,LinkedList底層是雙向循環鏈表
//存儲ArrayList元素的數組緩衝區
private transient Object[] elementData;
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;
    }
}
  1. ArrayList支持隨機訪問,linkedList不支持隨機訪問
  2. 使用下標訪問元素,ArrayList時間複雜度是O(1),LinkedList是O(n)
  3. 添加或刪除元素時,ArrayList時間複雜度爲O(n),LinkedList是O(1)
public boolean add(E e) {
    linkLast(e);
    return true;
}
void linkLast(E e) {
    final Node<E> l = last;
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}

☞ 如何實現數組和List之間的轉換?

數組轉換成List(轉換之後只能進行訪問不能進行刪除或者添加元素)(轉換之後爲什麼不增刪元素?

arr.asList();

List轉換成數組

list.toArray();

☞ ArrayList和Vector的區別是什麼?

ArrayList和Vector都是基於數組的實現,ArrayList非線程安全,Vector線程安全

☞ Array和ArrayList有何區別?

  1. Array可以容納基本類型和對象,ArrayList只能容納對象
  2. Array容量固定,ArrayList容量可以變化

☞ 在Queue中poll()和remove()有什麼區別?

poll()和remove()都是取出並刪除隊首的元素,區別是隊列爲空時,poll()返回null,而remove拋出異常

☞ 哪些集合類是線程安全的?

  • vector,向量:線程安全的數組
  • Stack,棧:一個線程安全的容器,繼承了vector,線程安全,特點是先進後出
  • HashTable:一個線程安全的map

☞ 迭代器Iterator?

  1. 迭代器Iterator是一種設計模式
  2. 迭代器Iterator用於遍歷容器中的元素
  3. 迭代器Iterator本身是一個接口,提供了三個方法:
boolean hasNext();
E next();
void remove();

但是具體的需要容器去實現,如hashmap:

private abstract class HashIterator<E> implements Iterator<E> {
    Entry<K,V> next;        // next entry to return
    int expectedModCount;   // For fast-fail
    int index;              // current slot
    Entry<K,V> current;     // current entry

    HashIterator() {
        expectedModCount = modCount;
        if (size > 0) { // advance to first entry
            Entry[] t = table;
            while (index < t.length && (next = t[index++]) == null)
                ;
        }
    }

    public final boolean hasNext() {
        return next != null;
    }

    final Entry<K,V> nextEntry() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        Entry<K,V> e = next;
        if (e == null)
            throw new NoSuchElementException();

        if ((next = e.next) == null) {
            Entry[] t = table;
            while (index < t.length && (next = t[index++]) == null)
                ;
        }
        current = e;
        return e;
    }

    public void remove() {
        if (current == null)
            throw new IllegalStateException();
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        Object k = current.key;
        current = null;
        HashMap.this.removeEntryForKey(k);
        expectedModCount = modCount;
    }
}

☞ Iterator怎麼使用?有什麼特點?

Iterator本身提供了三個核心方法:

//判斷是否存在下一個元素
boolean hasNext();
//取出下一個元素
E next();
//移除當前元素
void remove();

特點:單向移動,功能簡單

☞ Iterator和ListIterator有什麼區別?

  • Iterator可用來遍歷Set和List集合,ListIterator只能用來遍歷List
  • Iterator可以前向遍歷,ListIterator既可以前向遍歷也可以後向遍歷
  • ListIterator繼承於Iterator

☞ 怎麼確保一個集合不能被修改?

Collections包下的unmodifiableMap方法,裝飾傳入的map,當調用任何修改方法時,會拋出UnsupportedOperationException異常

Map<String, Object> map = new HashMap<>();
map.put("name", "lizza");
map.put("age", 18);
System.out.println(map);
map = Collections.unmodifiableMap(map);
map.put("name", "john");
System.out.println(map);

參考資料

【1】Java最常見的208道面試題及答案
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章