深入學習java原始碼之ArrayList.addAll()與ArrayList.retainAll()
深入學習java原始碼之ArrayList.addAll()與ArrayList.retainAll()
引入多型
List是介面,所以實現類要把介面中的抽象方法全部重寫。在重寫的時候父類中的方法的時候,操作的資料型別也是要與父類保持一致的。
所以父類和子類操作的都是泛型E(此時還不確定具體操作的是什麼資料型別,有使用者確定)
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{} List<String> list = new ArrayList<String>();
Java實現多型有三個必要條件:繼承、重寫、向上轉型。
重寫:子類對父類中某些方法進行重新定義,在呼叫這些方法時就會呼叫子類的方法。
向上轉型:在多型中需要將子類的引用賦給父類物件,只有這樣該引用才能夠具備技能呼叫父類的方法和子類的方法。
所謂多型就是指程式中定義的引用變數所指向的具體型別和通過該引用變數發出的方法呼叫在程式設計時並不確定,而是在程式執行期間才確定,即一個引用變數倒底會指向哪個類的例項物件,該引用變數發出的方法呼叫到底是哪個類中實現的方法,必須在由程式執行期間才能決定。
即不修改程式程式碼就可以改變程式執行時所繫結的具體程式碼,讓程式可以選擇多個執行狀態,這就是多型性。
在這裡我們這樣理解,這裡定義了一個List型別的list,它指向ArrayList物件例項。由於ArrayList是繼承與List,所以ArrayList可以自動向上轉型為List,所以list是可以指向ArrayList例項物件的。這樣做存在一個非常大的好處,在繼承中我們知道子類是父類的擴充套件,它可以提供比父類更加強大的功能,如果我們定義了一個指向子類的父類引用型別,那麼它除了能夠引用父類的共性外,還可以使用子類強大的功能。
但是向上轉型存在一些缺憾,那就是它必定會導致一些方法和屬性的丟失,而導致我們不能夠獲取它們。所以父類型別的引用可以呼叫父類中定義的所有屬性和方法,對於只存在與子類中的方法和屬性它就望塵莫及了。
指向子類的父類引用由於向上轉型了,它只能訪問父類中擁有的方法和屬性,而對於子類中存在而父類中不存在的方法,該引用是不能使用的,儘管是過載該方法。若子類重寫了父類中的某些方法,在呼叫該些方法的時候,必定是使用子類中定義的這些方法(動態連線、動態呼叫)。
引入泛型
使集合能夠記住集合內元素各型別,且能夠達到只要編譯時不出現問題,執行時就不會出現“java.lang.ClassCastException”異常
/* List list = new ArrayList(); list.add("qqyumidi"); list.add("corn"); list.add(100); for (int i = 0; i < list.size(); i++) { String name = (String) list.get(i); // 1 System.out.println("name:" + name); } */ List<String> list = new ArrayList<String>(); list.add("qqyumidi"); list.add("corn"); //list.add(100); // 1 提示編譯錯誤 for (int i = 0; i < list.size(); i++) { String name = list.get(i); // 2 System.out.println("name:" + name); }
通過List<String>,直接限定了list集合中只能含有String型別的元素,從而在//2處無須進行強制型別轉換,因為此時,集合能夠記住元素的型別資訊,編譯器已經能夠確認它是String型別了。
泛型定義,我們知道在List<String>中,String是型別實參,也就是說,相應的List介面中肯定含有型別形參。且get()方法的返回結果也直接是此形參型別(也就是對應的傳入的型別實參)
Java泛型中的標記符含義:
E - Element (在集合中使用,因為集合中存放的是元素)
ArrayList<Integer> list = new ArrayList<>();
T - Type(Java 類)
K - Key(鍵)
V - Value(值)
N - Number(數值型別)
? - 表示不確定的java型別
泛型三種:
[1]ArrayList<T> al=new ArrayList<T>();指定集合元素只能是T型別
[2]ArrayList<?> al=new ArrayList<?>();集合元素可以是任意型別,這種沒有意義,一般是方法中,只是為了說明用法
[3]ArrayList<? extends E> al=new ArrayList<? extends E>();
泛型的限定:
? extends E:接收E型別或者E的子型別。
?super E:接收E型別或者E的父型別。
在具體使用時,可以分為泛型介面、泛型類和泛型方法。
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
}
對於不同傳入的型別實參,生成的相應物件例項的型別是不是一樣的
class Box<T> {
private T data;
public Box() {
}
public Box(T data) {
this.data = data;
}
public T getData() {
return data;
}
}
Box<String> name = new Box<String>("corn");
Box<Integer> age = new Box<Integer>(712);
在使用泛型類時,雖然傳入了不同的泛型實參,但並沒有真正意義上生成不同的型別,傳入不同泛型實參的泛型類在記憶體上只有一個,即還是原來的最基本的型別(本例項中為Box),當然,在邏輯上我們可以理解成多個不同的泛型型別。
究其原因,在於Java中的泛型這一概念提出的目的,導致其只是作用於程式碼編譯階段,在編譯過程中,對於正確檢驗泛型結果後,會將泛型的相關資訊擦出,也就是說,成功編譯過後的class檔案中是不包含任何泛型資訊的。泛型資訊不會進入到執行時階段。
對此總結成一句話:泛型型別在邏輯上看以看成是多個不同的型別,實際上都是相同的基本型別。
java原始碼
可調整大小的陣列的實現List介面。 實現所有可選列表操作,並允許所有元素,包括null 。 除了實現List 介面之外,該類還提供了一些方法來操縱內部使用的儲存列表的陣列的大小。 (這個類是大致相當於Vector,不同之處在於它是不同步的)。
該size,isEmpty,get,set,iterator和listIterator操作在固定時間內執行。 add操作以攤餘常數執行 ,即新增n個元素需要O(n)個時間。 所有其他操作都以線性時間執行(粗略地說)。 與LinkedList實施相比,常數因子較低。
每個ArrayList例項都有一個容量 。 容量是用於儲存列表中的元素的陣列的大小。 它總是至少與列表大小一樣大。 當元素新增到ArrayList時,其容量會自動增長。 沒有規定增長政策的細節,除了新增元素具有不變的攤銷時間成本。
應用程式可以新增大量使用ensureCapacity操作元件的前增大ArrayList例項的容量。 這可能會減少增量重新分配的數量。
請注意,此實現不同步。 如果多個執行緒同時訪問884457282749例項,並且至少有一個執行緒在結構上修改列表,則必須在外部進行同步。 (結構修改是新增或刪除一個或多個元素的任何操作,或明確調整後臺陣列的大小;僅設定元素的值不是結構修改。)這通常是通過在一些自然地封裝了列表。 如果沒有這樣的物件存在,列表應該使用Collections.synchronizedList方法“包裝”。 這最好在建立時完成,以防止意外的不同步訪問列表:
List list = Collections.synchronizedList(new ArrayList(...)); The iterators returned by this class's個 iterator和listIterator方法是快速失敗的 :如果列表在任何時間從結構上修改建立迭代器之後,以任何方式除非通過迭代器自身remove種或add方法,迭代器都將丟擲一個ConcurrentModificationException 。 因此,面對併發修改,迭代器將快速而乾淨地失敗,而不是在未來未確定的時間冒著任意的非確定性行為。
請注意,迭代器的故障快速行為無法保證,因為一般來說,在不同步併發修改的情況下,無法做出任何硬性保證。 失敗快速迭代器盡力投入ConcurrentModificationException 。 因此,編寫依賴於此異常的程式的正確性將是錯誤的:迭代器的故障快速行為應僅用於檢測錯誤。
Modifier and Type | Method and Description |
---|---|
boolean |
add(E e) 將指定的元素追加到此列表的末尾。 |
void |
add(int index, E element) 在此列表中的指定位置插入指定的元素。 |
boolean |
addAll(Collection<? extends E> c) 按指定集合的Iterator返回的順序將指定集合中的所有元素追加到此列表的末尾。 |
boolean |
addAll(int index, Collection<? extends E> c) 將指定集合中的所有元素插入到此列表中,從指定的位置開始。 |
void |
clear() 從列表中刪除所有元素。 |
Object |
clone() 返回此 ArrayList例項的淺拷貝。 |
boolean |
contains(Object o) 如果此列表包含指定的元素,則返回 true 。 |
void |
ensureCapacity(int minCapacity) 如果需要,增加此 ArrayList例項的容量,以確保它可以至少儲存最小容量引數指定的元素數。 |
void |
forEach(Consumer<? super E> action) 對 |
E |
get(int index) 返回此列表中指定位置的元素。 |
int |
indexOf(Object o) 返回此列表中指定元素的第一次出現的索引,如果此列表不包含元素,則返回-1。 |
boolean |
isEmpty() 如果此列表不包含元素,則返回 true 。 |
int |
lastIndexOf(Object o) 返回此列表中指定元素的最後一次出現的索引,如果此列表不包含元素,則返回-1。 |
E |
remove(int index) 刪除該列表中指定位置的元素。 |
boolean |
remove(Object o) 從列表中刪除指定元素的第一個出現(如果存在)。 |
boolean |
removeAll(Collection<?> c) 從此列表中刪除指定集合中包含的所有元素。 |
boolean |
removeIf(Predicate<? super E> filter) 刪除滿足給定謂詞的此集合的所有元素。 |
protected void |
removeRange(int fromIndex, int toIndex) 從這個列表中刪除所有索引在 |
void |
replaceAll(UnaryOperator<E> operator) 將該列表的每個元素替換為將該運算子應用於該元素的結果。 |
boolean |
retainAll(Collection<?> c) 僅保留此列表中包含在指定集合中的元素。 |
E |
set(int index, E element) 用指定的元素替換此列表中指定位置的元素。 |
int |
size() 返回此列表中的元素數。 |
void |
sort(Comparator<? super E> c) 使用提供的 |
Object[] |
toArray() 以正確的順序(從第一個到最後一個元素)返回一個包含此列表中所有元素的陣列。 |
<T> T[] |
toArray(T[] a) 以正確的順序返回一個包含此列表中所有元素的陣列(從第一個到最後一個元素); 返回的陣列的執行時型別是指定陣列的執行時型別。 |
void |
trimToSize() 修改這個 ArrayList例項的容量是列表的當前大小。 |
package java.util;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
private static final long serialVersionUID = 8683452581122892189L;
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData; // non-private to simplify nested class access
private int size;
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It's already
// supposed to be at default size.
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
public int size() {
return size;
}
public boolean isEmpty() {
return size == 0;
}
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
// Positional Access Operations
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
private void fastRemove(int index) {
modCount++;
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
}
/**
* Removes all of the elements from this list. The list will
* be empty after this call returns.
*/
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);
size += numNew;
return numNew != 0;
}
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
Object[] a = c.toArray();
int numNew = a.length;
ensureCapacityInternal(size + numNew); // Increments modCount
int numMoved = size - index;
if (numMoved > 0)
System.arraycopy(elementData, index, elementData, index + numNew,
numMoved);
System.arraycopy(a, 0, elementData, index, numNew);
size += numNew;
return numNew != 0;
}
protected void removeRange(int fromIndex, int toIndex) {
modCount++;
int numMoved = size - toIndex;
System.arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved);
// clear to let GC do its work
int newSize = size - (toIndex-fromIndex);
for (int i = newSize; i < size; i++) {
elementData[i] = null;
}
size = newSize;
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, true);
}
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException{
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size);
// Write out all elements in the proper order.
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
/**
* Reconstitute the <tt>ArrayList</tt> instance from a stream (that is,
* deserialize it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
elementData = EMPTY_ELEMENTDATA;
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in capacity
s.readInt(); // ignored
if (size > 0) {
// be like clone(), allocate array based upon size not capacity
ensureCapacityInternal(size);
Object[] a = elementData;
// Read in all elements in the proper order.
for (int i=0; i<size; i++) {
a[i] = s.readObject();
}
}
}
@Override
public boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
// figure out which elements are to be removed
// any exception thrown from the filter predicate at this stage
// will leave the collection unmodified
int removeCount = 0;
final BitSet removeSet = new BitSet(size);
final int expectedModCount = modCount;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
@SuppressWarnings("unchecked")
final E element = (E) elementData[i];
if (filter.test(element)) {
removeSet.set(i);
removeCount++;
}
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
// shift surviving elements left over the spaces left by removed elements
final boolean anyToRemove = removeCount > 0;
if (anyToRemove) {
final int newSize = size - removeCount;
for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
i = removeSet.nextClearBit(i);
elementData[j] = elementData[i];
}
for (int k=newSize; k < size; k++) {
elementData[k] = null; // Let gc do its work
}
this.size = newSize;
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
return anyToRemove;
}
@Override
@SuppressWarnings("unchecked")
public void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator);
final int expectedModCount = modCount;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
elementData[i] = operator.apply((E) elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
@Override
@SuppressWarnings("unchecked")
public void sort(Comparator<? super E> c) {
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
}