深入學習java原始碼之ArrayList.iterator()與ArrayList.listIterator()
深入學習java原始碼之ArrayList.iterator()與ArrayList.listIterator()
內部類
可以將一個類定義在另一個類裡面或者一個方法裡面,這樣的類稱為內部類。廣泛意義上的內部類一般來說包括這四種:成員內部類、區域性內部類、匿名內部類和靜態內部類。下面就先來了解一下這四種內部類的用法。
成員內部類可以無條件訪問外部類的所有成員屬性和成員方法(包括private成員和靜態成員)。
內部類訪問外部類的變數必須宣告為final
方法中的區域性變數,方法結束後這個變數就要釋放掉,final保證這個變數始終指向一個物件。
首先,內部類和外部類其實是處於同一個級別,內部類不會因為定義在方法中就會隨著方法的執行完畢而跟隨者被銷燬。問題就來了,如果外部類的方法中的變數不定義final,那麼當外部類方法執行完畢的時候,這個區域性變數肯定也就被GC了,然而內部類的某個方法還沒有執行完,這個時候他所引用的外部變數已經找不到了。如果定義為final,java會將這個變數複製一份作為成員變數內置於內部類中,這樣的話,由於final所修飾的值始終無法改變,所以這個變數所指向的記憶體區域就不會變。
注意,若使用JDK1.8,方法中內部類的方法是可以直接訪問外部類的方法的區域性變數,並且不需要宣告為final型別。
不過要注意的是,當成員內部類擁有和外部類同名的成員變數或者方法時,會發生隱藏現象,即預設情況下訪問的是成員內部類的成員。如果要訪問外部類的同名成員,需要以下面的形式進行訪問:
外部類.this.成員變數
外部類.this.成員方法
內部類是依附外部類而存在的,也就是說,如果要建立成員內部類的物件,前提是必須存在一個外部類的物件。建立成員內部類物件的一般方式如下:
//第一種方式: Outter outter = new Outter(); Outter.Inner inner = outter.new Inner(); //必須通過Outter物件來建立 //第二種方式: Outter.Inner inner1 = outter.getInnerInstance();
如果想在Static方法中new內部類,可以把內部類宣告為Static
public class OuterClass { private void outerMethod() { System.out.println("It's Method of OuterClass"); } public static void main(String[] args) { Innerclass in = new Innerclass(); in.innerMethod(); } static class Innerclass {//把內部類宣告為static public void innerMethod() { System.out.println("It's Method of innerMethod"); } }
當然,一般不使用static的方式,而是推薦這種方法:x.new A() ,其中 x是外部類OuterClass的例項,A是內部類Innerclass
package innerclass;
public class OuterClass {
private void outerMethod() {
System.out.println("It's Method of OuterClass");
}
public static void main(String[] args) {
OuterClass.Innerclass in = new OuterClass().new Innerclass();//使用x.new A()的方式
in.innerMethod();
}
class Innerclass {
public void innerMethod() {
System.out.println("It's Method of innerMethod");
}
}
x.new A() ,其中 x是外部類OuterClass的例項,A是類部類Innerclass,當然可以拆分如下,這樣就顯然很明白啦:
public static void main(String[] args) {
OuterClass out = new OuterClass();//外部例項
OuterClass.Innerclass in = out.new Innerclass();//外部例項.new 外部類
in.innerMethod();
}
內部類的使用典型的情況是,內部類繼承自某個類或實現某個介面,內部類的程式碼操作建立其的外層類的物件。所以你可以認為內部類提供了某種進入其外層類的視窗。
使用內部類最吸引人的原因是:每個內部類都能獨立地繼承自一個(介面的)實現,所以無論外層類是否已經繼承了某個(介面的)實現,對於內部類都沒有影響。如果沒有內部類提供的可以繼承多個具體的或抽象的類的能力,一些設計與程式設計問題就很難解決。從這個角度看,內部類使得多重繼承的解決方案變得完整。介面解決了部分問題,而內部類有效地實現了“多重繼承”。
ArrayList<String> list = new ArrayList<String>();
list.add(null);
list.add("abc1");
list.add("abc2");
list.add("abc3");
list.add("abc4");
/*
* 使用迭代器遍歷集合
*/
//獲取迭代器
Iterator<String> it = list.iterator();
//使用while遍歷集合
while(it.hasNext()){
String s = it.next();
/*
* 判斷集合中有沒有"abc3"這個元素
* 如果有,增加一個元素"itcast"
* 程式設計技巧:使用equals判斷的時候,要把已知的變數寫在前邊,未知的寫在後邊,防止空指標異常
*/
//if(s.equals("abc3")){
if("abc3".equals(s)){
//1.迭代就是迭代,不要對集合進行修改
//list.add("itcast");
}
System.out.println(s);
}
/*
* 2.使用迭代器Iterator的子介面ListIterator中的方法add/remove,讓迭代器自己增加往集合中增加元素/移除元素
*/
ListIterator<String> listIt = list.listIterator();
while(listIt.hasNext()){
String s = listIt.next();
if("abc3".equals(s)){
listIt.add("itcast");
}
System.out.println(s);
}
System.out.println(list);
}
java原始碼
一個集合的迭代器。 Iterator需要的地方Enumeration在Java集合框架。 迭代器有兩種不同的列舉方式:
迭代器允許呼叫者在迭代期間從底層集合中刪除元素,並具有明確定義的語義。
方法名稱得到改進。
Modifier and Type | Method and Description |
---|---|
void |
forEach(Consumer<? super E> action) 對 |
Iterator<E> |
iterator() 以正確的順序返回該列表中的元素的迭代器。 |
ListIterator<E> |
listIterator() 返回列表中的列表迭代器(按適當的順序)。 |
ListIterator<E> |
listIterator(int index) 從列表中的指定位置開始,返回列表中的元素(按正確順序)的列表迭代器。 |
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 ListIterator<E> listIterator(int index) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index);
}
public ListIterator<E> listIterator() {
return new ListItr(0);
}
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
private class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) {
super();
cursor = index;
}
public boolean hasPrevious() {
return cursor != 0;
}
public int nextIndex() {
return cursor;
}
public int previousIndex() {
return cursor - 1;
}
@SuppressWarnings("unchecked")
public E previous() {
checkForComodification();
int i = cursor - 1;
if (i < 0)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i;
return (E) elementData[lastRet = i];
}
public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.set(lastRet, e);
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
public void add(E e) {
checkForComodification();
try {
int i = cursor;
ArrayList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
}
ConcurrentModificationException 異常
當不允許這樣的修改時,可以通過檢測到物件的併發修改的方法來丟擲此異常。
例如,一個執行緒通常不允許修改集合,而另一個執行緒正在遍歷它。 一般來說,在這種情況下,迭代的結果是未定義的。 某些迭代器實現(包括由JRE提供的所有通用集合實現的實現)可能會選擇在檢測到此行為時丟擲此異常。 這樣做的迭代器被稱為故障快速迭代器,因為它們快速而乾淨地失敗,而是在未來未確定的時間冒著任意的非確定性行為。
請注意,此異常並不總是表示物件已被不同的執行緒同時修改。 如果單個執行緒發出違反物件合同的方法呼叫序列,則該物件可能會丟擲此異常。 例如,如果執行緒在使用故障快速迭代器迭代集合時直接修改集合,則迭代器將丟擲此異常。
請注意,故障快速行為無法保證,因為一般來說,在不同步併發修改的情況下,無法做出任何硬性保證。 失敗快速的操作儘可能地丟擲ConcurrentModificationException 。 因此,編寫依賴於此異常的程式的正確性將是錯誤的: ConcurrentModificationException應僅用於檢測錯誤。
package java.util;
public class ConcurrentModificationException extends RuntimeException {
private static final long serialVersionUID = -3666751008965953603L;
public ConcurrentModificationException() {
}
public ConcurrentModificationException(String message) {
super(message);
}
public ConcurrentModificationException(Throwable cause) {
super(cause);
}
public ConcurrentModificationException(String message, Throwable cause) {
super(message, cause);
}
}
Iterator
public interface Iterator<E>一個集合的迭代器。 Iterator需要的地方Enumeration在Java集合框架。 迭代器有兩種不同的列舉方式:
迭代器允許呼叫者在迭代期間從底層集合中刪除元素,並具有明確定義的語義。
方法名稱得到改進。
此介面是成員Java Collections Framework 。
Modifier and Type | Method and Description |
---|---|
default void |
forEachRemaining(Consumer<? super E> action) 對每個剩餘元素執行給定的操作,直到所有元素都被處理或動作引發異常。 |
boolean |
hasNext() 如果迭代具有更多元素,則返回 |
E |
next() 返回迭代中的下一個元素。 |
default void |
remove() 從底層集合中刪除此迭代器返回的最後一個元素(可選操作)。 |
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> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
用於允許程式設計師沿任一方向遍歷列表的列表的迭代器,在迭代期間修改列表,並獲取列表中迭代器的當前位置。 A ListIterator沒有電流元素; 其游標位置始終位於通過呼叫previous()返回的元素和通過呼叫next()返回的元素next() 。 長度為n的列表的迭代器具有n+1可能的游標位置,如下圖所示的^ ( ^ )所示:
Element(0) Element(1) Element(2) ... Element(n-1)
cursor positions: ^ ^ ^ ^ ^ 請注意, remove()和set(Object)方法未按游標位置進行定義; 它們被定義為對呼叫next()或previous()返回的最後一個元素進行操作。
Modifier and Type | Method and Description |
---|---|
void |
add(E e) 將指定的元素插入列表(可選操作)。 |
boolean |
hasNext() 返回 |
boolean |
hasPrevious() 返回 |
E |
next() 返回列表中的下一個元素,並且前進游標位置。 |
int |
nextIndex() 返回隨後呼叫 |
E |
previous() 返回列表中的上一個元素,並向後移動游標位置。 |
int |
previousIndex() 返回由後續呼叫 |
void |
remove() 從列表中刪除由 |
void |
set(E e) 用 指定的元素替換由 |
package java.util;
public interface ListIterator<E> extends Iterator<E> {
// Query Operations
boolean hasNext();
E next();
boolean hasPrevious();
E previous();
int nextIndex();
int previousIndex();
// Modification Operations
void remove();
void set(E e);
void add(E e);
}