基本概念
Java的集合類,也可以叫做容器類,用來“保存對象”。它有兩種:
Collection
如果保存的對象是單個對象,就是 Collection 類。Collection 類就是一個接口。先看看它的實現類和子接口。
這裏面最重要的子接口是:Set, List, Queue. JDK 文檔說了,JDK不提供Collection接口的直接實現。
看到這裏挺納悶的,明明有那麼多類實現了Collection接口,這不是亂說嗎?難道是老的JDK有沒有直接實現,新的JDK已經有直接實現了,帶着問題小心求證一下:
JDK7: https://docs.oracle.com/javase/7/docs/api/java/util/Collection.html
The JDK does not provide any direct implementations of this interface: it provides implementations of more specific subinterfaces like Set and List.
JDK8: https://docs.oracle.com/javase/8/docs/api/java/util/Collection.html
The JDK does not provide any direct implementations of this interface: it provides implementations of more specific subinterfaces like Set and List.
JDK9: https://docs.oracle.com/javase/9/docs/api/java/util/Collection.html
The JDK does not provide any direct implementations of this interface: it provides implementations of more specific subinterfaces like Set and List.
JDK10: https://docs.oracle.com/javase/10/docs/api/java/util/Collection.html
The JDK does not provide any direct implementations of this interface: it provides implementations of more specific subinterfaces like Set and List.
事實結果說明我的假設是錯的。
Class name | type | related jar | description |
---|---|---|---|
ValuesView in ConcurrentHashMap (java.util.concurrent) | inner class | JDK rt.jar | 沒有實現add 和 addAll 方法 |
BeanContext (java.beans.beancontext) | interface | JDK rt.jar | public interface BeanContext extends BeanContextChild, Collection, DesignMode, Visibility |
Multiset (com.google.common.collect) | interface | guava-27.0.1-jre.jar | 一個像Set一樣可以保存元素的集合,不過它可以保存重複的元素,這種集合又稱之爲Bag |
CheckedCollection in Collections (java.util) | inner class | JDK rt.jar | 一個代理類,在創建的時候傳入被代理的Collection對象 |
Set (java.util) | interface | JDK rt.jar | 不包含重複元素的集合。 更正式的是,集不包含一對元素 e1 和 e2,使得 e1.equal(e2),最多包含一個null元素。 顧名思義,此接口對數學集抽象進行模型。 |
SynchronizedCollection in Synchronized (com.google.common.collect) | inner class | guava-27.0.1.jar | static class SynchronizedCollection extends SynchronizedObject implements Collection 通過 synchronized (mutex) {}的方式給代理對象加鎖 |
SynchronizedCollection in Collections (java.util) | inner class | rt.jar | static class SynchronizedCollection implements Collection, Serializable 通過 synchronized (mutex) {}的方式給代理對象加鎖 |
ContextStack in ThreadContext (org.apache.logging.log4j) | inner interface | log4j-api.2.11.2.jar | The ThreadContext Stack interface. |
AbstractCollection (java.util) | abstract class | JDK rt.jar | 沒有實現add(E var1)等方法,會拋UnsupportedOperationException |
List (java.util) | interface | rt,jar | 有序集合(也稱爲序列)。該界面的用戶可以精確控制列表中每個元素的插入位置。用戶可以通過其整數索引(列表中的位置)訪問元素,並在列表中搜索元素。 |
CollectionView in ConcurrentHashMap (java.util.concurrent) | inner abstract static class | JDK rt.jar | 沒有實現完Collection接口的方法。 |
ForwardingCollection (com.google.common.collect) | abstract class | abstract class | 裝飾器模式,允許用戶向現有對象添加新功能,而無需更改其結構。這種設計模式屬於結構模式,因爲該模式充當現有類的包裝。 |
UnmodifiableCollection in Collections (java.util) | innner static class | rt.jar | 包含一個final屬性final Collection<? extends E> c; |
Queue (java.util) | interface | rt.jar | 設計用於在處理之前容納元素的集合。除了基本Collection操作外,隊列還提供其他插入,提取和檢查操作。這些方法中的每一種都以兩種形式存在:一種在操作失敗時引發異常,另一種返回一個特殊值(null或false,取決於操作)。插入操作的後一種形式是專門爲限制容量的Queue 實現而設計的。在大多數實現中,插入操作不會失敗。 |
可以看出,Collection 接口最重要的子接口就是List, Set 和 Queue 了。
先看看Collection的所有方法:
List
有序集合(也稱爲序列)。該界面的用戶可以精確控制列表中每個元素的插入位置。用戶可以通過其整數索引(列表中的位置)訪問元素,並在列表中搜索元素。
和 Set 相比:
- List 允許重複的元素, 也就是說,e1.equals(e2), 可以插入e1 和 e2.
- List 如果完全允許使用空元素,則通常允許使用多個空元素。允許插入多個 null 元素.
List 相較於 Collection 接口,擴展了根據索引位置操作元素的方法: - get(int)
- set(int, E)
- add(int, E)
- remove(int)
List 還提供了根據給的對象找索引的方法。
- indexOf(Object). 查到第一次出現給定對象的索引位置。
- lastIndexOf(Object) , 查找List當中最後一次出現給定對象的位置。
還有返回ListIterator的方法:
- listIterator() 返回此列表中的元素的列表迭代器(按適當順序) ListIterator。
- listIterator(int) 從列表中的指定位置開始,以適當的順序返回在此列表中的元素上的列表迭代器 ListIterator。
插一句嘴,Iterator 和 Iterable 的區別:
package java.util;
import java.util.function.Consumer;
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer<? super E> var1) {
Objects.requireNonNull(var1);
while(this.hasNext()) {
var1.accept(this.next());
}
}
}
package java.lang;
import java.util.Iterator;
import java.util.Objects;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.function.Consumer;
public interface Iterable<T> {
Iterator<T> iterator();
default void forEach(Consumer<? super T> var1) {
Objects.requireNonNull(var1);
Iterator var2 = this.iterator();
while(var2.hasNext()) {
Object var3 = var2.next();
var1.accept(var3);
}
}
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(this.iterator(), 0);
}
}
Iterator 有方法:
- hasNext() 如果迭代具有更多元素,則返回true。
- next() 返回迭代中的下一個元素。
- remove() 從基礎集合中移除此迭代器返回的最後一個元素(可選操作)。
- forEachRemaining(Consumer<? super E> action) 對剩餘的每個元素執行給定的操作,直到所有元素都已處理或該操作引發異常。
Iterable 有方法: - default void forEach(Consumer<? super T> action) 對中的每個元素執行給定的操作,Iterable 直到所有元素都已處理或該操作引發異常。
- Iterator iterator() 返回類型爲的元素上的迭代器T。
- default Spliterator spliterator() Spliterator在this所描述的元素上 創建一個Iterable。
從java9開始,List 還提供一堆of方法,就是返回一個大小不可變的List. 代碼實現是用了ImmutableCollections類的方法。
比如返回有N個原屬構成的大小固定的List
看到沒有,爲了達到大小不可變,本質上就是用final修飾了: private final E[] elements;
static final class ListN<E> extends AbstractImmutableList<E>
implements Serializable {
// EMPTY_LIST may be initialized from the CDS archive.
static @Stable List<?> EMPTY_LIST;
static {
VM.initializeFromArchive(ListN.class);
if (EMPTY_LIST == null) {
EMPTY_LIST = new ListN<>();
}
}
@Stable
private final E[] elements;
@SafeVarargs
ListN(E... input) {
// copy and check manually to avoid TOCTOU
@SuppressWarnings("unchecked")
E[] tmp = (E[])new Object[input.length]; // implicit nullcheck of input
for (int i = 0; i < input.length; i++) {
tmp[i] = Objects.requireNonNull(input[i]);
}
elements = tmp;
}
@Override
public boolean isEmpty() {
return elements.length == 0;
}
@Override
public int size() {
return elements.length;
}
@Override
public E get(int index) {
return elements[index];
}
@java.io.Serial
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
throw new InvalidObjectException("not serial proxy");
}
@java.io.Serial
private Object writeReplace() {
return new CollSer(CollSer.IMM_LIST, elements);
}
@Override
public Object[] toArray() {
return Arrays.copyOf(elements, elements.length);
}
@Override
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
int size = elements.length;
if (a.length < size) {
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elements, size, a.getClass());
}
System.arraycopy(elements, 0, a, 0, size);
if (a.length > size) {
a[size] = null; // null-terminate
}
return a;
}
}
關於Stable註解的解釋
如果字段的所有組件變量最多更改一次值,則字段可能被批號爲穩定字段。字段的值計爲其組件值。如果將字段鍵入爲數組,則數組的所有非空組件(深度到字段的數組類型排名)也計爲組件值。通過擴展,任何已批過爲穩定的變量(數組或字段)稱爲穩定變量,其非空值或非零值稱爲穩定值。由於所有字段的引用默認值爲 null(對基元爲零),因此此註釋指示存儲在字段中的第一個非空值(resp.,非零)值永遠不會更改。如果字段不是數組類型,則沒有數組元素,則指示爲穩定的值只是字段的值。如果字段值的動態類型是數組,但靜態類型不是,則數組的組件不是被視爲穩定。如果字段是數組類型,則字段值和字段值的所有組件(如果字段值爲非空)都指示爲穩定。如果字段類型是排名 [@code N = 1] 的數組類型,則字段值的每個組件(如果字段值是非 null)被視爲排名 [@code N-1] 的穩定數組。聲明 [@code 最終] 的字段也可以被條號爲穩定字段。由於最終字段已作爲穩定值,因此此類註釋不會傳達有關字段值更改的其他信息,但如果字段的類型是數組類型()如上所述)。HotSpot VM 依賴於此註釋將非空(resp.,非零)組件值提升爲常量,從而根據此類值(如常量摺疊)實現代碼的卓越優化。更具體地說,HotSpot VM 將以與靜態最終字段類似的方式處理非空穩定字段(最終或其他),從而將字段的值提升爲常量。 因此,撇開空/非空值和數組的差異不談,最終的穩定字段將被視爲 Java 語言和 HotSpot VM 的最終字段。如果爲被批示爲穩定的字段提供第三個值(通過顯式更新穩定字段、穩定數組的組件或通過反射或其他方式更新最終穩定字段),則會發生什麼情況(當前)未定義。由於 HotSpot VM 將非空組件值提升爲常量,因此,如果此類常量(字段的第二個值)用作字段的值(更改爲第三個值),則 Java 內存模型可能會顯示爲損壞).@implNote此註釋僅適用於引導加載程序加載的類的字段。 將忽略在引導加載程序之外加載的類字段上的註釋。