先回顧一下ArrayList的類定義java
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
複製代碼
接口/類新增方法算法
public interface Collection<E> extends Iterable<E> {
.....
//1.8新增方法:提供了接口默認實現,返回分片迭代器
@Override
default Spliterator<E> spliterator() {
return Spliterators.spliterator(this, 0);
}
//1.8新增方法:提供了接口默認實現,返回串行流對象
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
//1.8新增方法:提供了接口默認實現,返回並行流對象
default Stream<E> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
/**
* Removes all of the elements of this collection that satisfy the given
* predicate. Errors or runtime exceptions thrown during iteration or by
* the predicate are relayed to the caller.
* 1.8新增方法:提供了接口默認實現,移除集合內全部匹配規則的元素,支持Lambda表達式
*/
default boolean removeIf(Predicate<? super E> filter) {
//空指針校驗
Objects.requireNonNull(filter);
//注意:JDK官方推薦的遍歷方式仍是Iterator,雖然forEach是直接用for循環
boolean removed = false;
final Iterator<E> each = iterator();
while (each.hasNext()) {
if (filter.test(each.next())) {
each.remove();//移除元素必須選用Iterator.remove()方法
removed = true;//一旦有一個移除成功,就返回true
}
}
//這裏補充一下:因爲一旦出現移除失敗將拋出異常,所以返回false指的僅僅是沒有匹配到任何元素而不是運行異常
return removed;
}
}
public interface Iterable<T>{
.....
//1.8新增方法:提供了接口默認實現,用於遍歷集合
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
//1.8新增方法:提供了接口默認實現,返回分片迭代器
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}
public interface List<E> extends Collection<E> {
//1.8新增方法:提供了接口默認實現,用於對集合進行排序,主要是方便Lambda表達式
default void sort(Comparator<? super E> c) {
Object[] a = this.toArray();
Arrays.sort(a, (Comparator) c);
ListIterator<E> i = this.listIterator();
for (Object e : a) {
i.next();
i.set((E) e);
}
}
//1.8新增方法:提供了接口默認實現,支持批量刪除,主要是方便Lambda表達式
default void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator);
final ListIterator<E> li = this.listIterator();
while (li.hasNext()) {
li.set(operator.apply(li.next()));
}
}
/**
* 1.8新增方法:返回ListIterator實例對象
* 1.8專門爲List提供了專門的ListIterator,相比於Iterator主要有如下加強:
* 1.ListIterator新增hasPrevious()和previous()方法,從而能夠實現逆向遍歷
* 2.ListIterator新增nextIndex()和previousIndex()方法,加強了其索引定位的能力
* 3.ListIterator新增set()方法,能夠實現對象的修改
* 4.ListIterator新增add()方法,能夠向List中添加對象
*/
ListIterator<E> listIterator();
}
複製代碼
1.8的ArrayList
只是在1.7的基礎上作了不多的改動,主要集中於初始化以及實現接口新增方法方面
1.7版本請參見筆者的 集合番@ArrayList一文通(1.7版)
全局變量的變動數組
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer.
* Any empty ArrayList with elementData == EMPTY_ELEMENTDATA will be expanded to
* DEFAULT_CAPACITY when the first element is added.
* 數組緩存,跟1.7版本相比,主要有兩個變化:
* 1.去掉private屬性,使用默認的friendly做用域,開放給同包類使用
* 2.一個空數組的elementData將設置爲EMPTY_ELEMENTDATA直到第一個元素新增時
* 使用DEFAULT_CAPACITY(10)完成有容量的初始化 -- 優化:這裏選擇將內存分配後置,而從儘量節省空間
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* Shared empty array instance used for empty instances.
* 當時用空構造時,給予數組(elementData)默認值
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
複製代碼
構造器的變動緩存
/**
* Constructs an empty list with an initial capacity of ten.
* 1.8版的默認構造器,只會初始化一個空數組
*/
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;//初始化一個空數組
}
/**
* Constructs an empty list with an initial capacity of ten.
* 1.7版的默認構造器,會直接初始化一個10容量的數組
*/
public ArrayList() {
this(10);
}
複製代碼
trimToSize方法變動安全
/**
* 1.8版的trimToSize,跟1.7版相比:
* 能夠明顯的看到去掉了oldCapacity這一臨時變量
* 筆者認爲這進一步強調了HashMap是非線程安全的,所以直接用length便可
*/
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = Arrays.copyOf(elementData, size);
}
}
/**
* 1.7版的trimToSize
*/
public void trimToSize() {
modCount++;
int oldCapacity = elementData.length;
if (size < oldCapacity) {
elementData = Arrays.copyOf(elementData, size);
}
}
複製代碼
/**
* Performs the given action for each element of the {@code Iterable}
* until all elements have been processed or the action throws an
* exception. Unless otherwise specified by the implementing class,
* actions are performed in the order of iteration (if an iteration order
* is specified). Exceptions thrown by the action are relayed to the
* caller.
* 1.8新增方法,重寫Iterable接口的forEach方法
* 提供對數組的遍歷操做,因爲支持Consumer所以在遍歷時將執行傳入的方法
*/
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int expectedModCount = modCount;
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) this.elementData;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) {
action.accept(elementData[i]);//執行傳入的自定義方法
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
--------------
List<String> list = new ArrayList<>();
list.add("有村架純");
list.add("橋本環奈");
list.add("齋藤飛鳥");
list.forEach(s -> System.out.print(s + "!!")); //輸出:有村架純!!橋本環奈!!齋藤飛鳥!!
複製代碼
/**
* Removes all of the elements of this collection that satisfy the given
* predicate. Errors or runtime exceptions thrown during iteration or by
* the predicate are relayed to the caller.
* 1.8新增方法,重寫Collection接口的removeIf方法
* 移除集合內全部複合匹配條件的元素,迭代時報錯會拋出異常 或 把斷言傳遞給調用者(即斷言中斷)
* 該方法主要乾了兩件事情:
* 1.根據匹配規則找到全部符合要求的元素
* 2.移除元素並轉移剩餘元素位置
* 補充:爲了安全和快速,removeIf分紅兩步走,而不是直接找到就執行刪除和轉移操做,寫法值得借鑑
*/
@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;
//BitSet用於按位存儲,這裏用做存儲待移除元素(即符合匹配規則的元素)
//BitSet可以經過位圖算法大幅減小數據佔用存儲空間和內存,尤爲適合在海量數據方面,這裏是個很明顯的優化
//有機會會在基礎番中解析一下BitSet的奇妙之處
final BitSet removeSet = new BitSet(size);
final int expectedModCount = modCount;
final int size = this.size;
//每次循環都要判斷modCount == expectedModCount!
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++;
}
//正常狀況下,一旦匹配到元素,應該刪除成功,不然將拋出異常,當沒有匹配到任何元素時,返回false
return anyToRemove;
}
--------------
List<String> list = new ArrayList<>();
list.add("有村架純");
list.add("橋本環奈");
list.add("齋藤飛鳥");
list.forEach(s -> System.out.print(s + "!!")); //輸出:有村架純!!橋本環奈!!齋藤飛鳥!!
System.out.println(list.removeIf(s -> s.startsWith("齋藤")));//輸出:true
list.forEach(s -> System.out.print(s + ",")); //輸出:有村架純!!橋本環奈!!
--------------
//這裏補充一點,使用Arrays.asList()生成的ArrayList是Arrays本身的私有靜態內部類
//強行使用removeIf的話會拋出java.lang.UnsupportedOperationException的異常(由於它沒實現這個方法)
複製代碼
/**
* Replaces each element of this list with the result of applying the operator to that element.
* Errors or runtime exceptions thrown by the operator are relayed to the caller.
* 1.8新增方法,重寫List接口的replaceAll方法
* 提供支持一元操做的批量替換功能
*/
@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++;
}
--------------
List<String> list = new ArrayList<>();
list.add("有村架純");
list.add("橋本環奈");
list.add("齋藤飛鳥");
list.forEach(s -> System.out.print(s + "!!")); //輸出:有村架純!!橋本環奈!!齋藤飛鳥!!
list.replaceAll(t -> {
if(t.equals("橋本環奈")) t = "逢澤莉娜";//這裏咱們將"橋本環奈"替換成"逢澤莉娜"
return t;//注意若是是語句塊的話必定要返回
});
list.forEach(s -> System.out.print(s + "!!")); //輸出:有村架純!!逢澤莉娜!!齋藤飛鳥!!
複製代碼
/**
* Sorts this list according to the order induced by the specified
* 1.8新增方法,重寫List接口的sort方法
* 支持對數組進行排序,主要方便於Lambda表達式
*/
@Override
@SuppressWarnings("unchecked")
public void sort(Comparator<? super E> c) {
final int expectedModCount = modCount;
//Arrays.sort底層是結合歸併排序和插入排序的混合排序算法,有不錯的性能
//有機會在基礎番對Timsort(1.8版)和ComparableTimSort(1.7版)進行解析
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
--------------
List<String> list = new ArrayList<>();
list.add("有村架純");
list.add("橋本環奈");
list.add("齋藤飛鳥");
list.forEach(s -> System.out.print(s + "!!")); //輸出:有村架純!!橋本環奈!!齋藤飛鳥!!
list.sort((prev, next) -> prev.compareTo(next));//這裏咱們選用天然排序
list.forEach(s -> System.out.print(s + "!!"));//輸出:齋藤飛鳥!!有村架純!!橋本環奈!!
複製代碼
什麼是並行分片迭代器?bash
ArrayListSpliterator類解析併發
default Stream<E> parallelStream() {//並行流
return StreamSupport.stream(spliterator(), true);//true表示使用並行處理
}
static final class ArrayListSpliterator<E> implements Spliterator<E> {
private final ArrayList<E> list;
//起始位置(包含),advance/split操做時會修改
private int index; // current index, modified on advance/split
//結束位置(不包含),-1 表示到最後一個元素
private int fence; // -1 until used; then one past last index
private int expectedModCount; // initialized when fence set
/** Create new spliterator covering the given range */
ArrayListSpliterator(ArrayList<E> list, int origin, int fence,
int expectedModCount) {
this.list = list; // OK if null unless traversed
this.index = origin;
this.fence = fence;
this.expectedModCount = expectedModCount;
}
/**
* 獲取結束位置,主要用於第一次使用時對fence的初始化賦值
*/
private int getFence() { // initialize fence to size on first use
int hi; // (a specialized variant appears in method forEach)
ArrayList<E> lst;
if ((hi = fence) < 0) {
//當list爲空,fence=0
if ((lst = list) == null)
hi = fence = 0;
else {
//不然,fence = list的長度
expectedModCount = lst.modCount;
hi = fence = lst.size;
}
}
return hi;
}
/**
* 對任務(list)分割,返回一個新的Spliterator迭代器
*/
public ArrayListSpliterator<E> trySplit() {
//二分法
int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
return (lo >= mid) ? null : // divide range in half unless too small 分紅兩部分,除非不夠分
new ArrayListSpliterator<E>(list, lo, index = mid,expectedModCount);
}
/**
* 對單個元素執行給定的執行方法
* 若沒有元素須要執行,返回false;若可能還有元素還沒有執行,返回true
*/
public boolean tryAdvance(Consumer<? super E> action) {
if (action == null)
throw new NullPointerException();
int hi = getFence(), i = index;
if (i < hi) {//起始位置 < 終止位置 -> 說明還有元素還沒有執行
index = i + 1; //起始位置後移一位
@SuppressWarnings("unchecked") E e = (E)list.elementData[i];
action.accept(e);//執行給定的方法
if (list.modCount != expectedModCount)
throw new ConcurrentModificationException();
return true;
}
return false;
}
/**
* 對每一個元素執行給定的方法,依次處理,直到全部元素已被處理或被異常終止
* 默認方法調用tryAdvance方法
*/
public void forEachRemaining(Consumer<? super E> action) {
int i, hi, mc; // hoist accesses and checks from loop
ArrayList<E> lst; Object[] a;
if (action == null)
throw new NullPointerException();
if ((lst = list) != null && (a = lst.elementData) != null) {
if ((hi = fence) < 0) {
mc = lst.modCount;
hi = lst.size;
}
else
mc = expectedModCount;
if ((i = index) >= 0 && (index = hi) <= a.length) {
for (; i < hi; ++i) {
@SuppressWarnings("unchecked") E e = (E) a[i];
action.accept(e);
}
if (lst.modCount == mc)
return;
}
}
throw new ConcurrentModificationException();
}
/**
* 計算還沒有執行的任務個數
*/
public long estimateSize() {
return (long) (getFence() - index);
}
/**
* 返回當前對象的特徵量
*/
public int characteristics() {
return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
}
}
--------------
List<String> list = new ArrayList<>();
list.add("有村架純");
list.add("橋本環奈");
list.add("齋藤飛鳥");
Spliterator<String> spliterator = list.spliterator();
spliterator.forEachRemaining(s -> System.out.print(s += "妹子!!"));
//輸出:有村架純妹子!!橋本環奈妹子!!齋藤飛鳥妹子!!
//由於這個類是提供給Stream使用的,所以能夠直接用Stream,下面的代碼做用等同上面,但進行了併發優化
Stream<String> parallelStream = list.parallelStream();
parallelStream.forEach(s -> System.out.print(s += "妹子!!"));
//輸出:橋本環奈妹子!!有村架純妹子!!齋藤飛鳥妹子!! --> 由於引入併發,全部執行順序會有些不一樣
複製代碼