前言
作為Java集合部分最後一小塊内容,我這裡決定談談兩個方法subList和asList方法,書上對這兩個方法的講解不多,有幾點是需要我們注意的
subList
功能:subList和subString的功能一樣,都是用來切割用的。但是它們兩者還是有微小的差別的。讓我們通過執行個體來看看
視圖
public class SubListTest {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add();
list.add();
List<Integer> list2 = new ArrayList<>(list);
List<Integer> list3 = list2.subList(, list2.size());
System.out.println(list2);
// 證明subList傳回的是新對象
System.out.println(list2 == list3);
list3.add();
System.out.println("list2: "+list2);
System.out.println("list3: "+list3);
}
}
----結果----
[, ]
false
list2: [, , ]
list3: [, , ]
解讀一下:
通過list2建立了一個subList并且修改list3,可以看到list2也同時改變了,這種視圖結構我們需要去看看底層的實作
源碼
- subList方法
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList<>(this, fromIndex, toIndex);
}
這部分是關鍵,subList傳回的是SubList對象沒錯,但是同時把this指針作為參數傳遞到了構造方法
- 構造方法
private static class SubList<E> extends AbstractList<E> implements RandomAccess {
private final ArrayList<E> root;
private final SubList<E> parent;
private final int offset;
private int size;
/**
* Constructs a sublist of an arbitrary ArrayList.
*/
public SubList(ArrayList<E> root, int fromIndex, int toIndex) {
this.root = root;
this.parent = null;
this.offset = fromIndex;
this.size = toIndex - fromIndex;
this.modCount = root.modCount;
}
- add
public void add(int index, E element) {
rangeCheckForAdd(index);
checkForComodification();
root.add(offset + index, element);
updateSizeAndModCount();
}
我們可以從源碼中得出結論,所有的操作其實最終都直接在原表中直接進行操作的!它就像一個代理。将對自己的操作全部發送到原表上。
modCount鎖定原表
生成子清單之後,不能再動原表
public class SubListTest {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add();
list.add();
List<Integer> list3 = list.subList(, list.size());
list.add();
System.out.println(list.toString() + list3.toString());
}
}
----結果----
Exception in thread "main" java.util.ConcurrentModificationException
啥意思呢?意思就是說,當用了subList之後,原表的再次改動會讓子表無法動彈。什麼事也做不了,還記得我們之前的源碼中都會看到這樣一個方法,checkForComodification()。
- checkForComodification()
private void checkForComodification() {
if (root.modCount != modCount)
throw new ConcurrentModificationException();
}
如果原清單的值和我的值改動不一樣則直接報錯。
推薦
如果需要對清單的局部處理的化,subList會是一個比較好的選擇,因為它會直接作用在原表上,很好用。
Aslist
基本類型和包裝類型
public class AslistTest {
public static void main(String[] args) {
int[] inta = {, , , };
// 泛型寫成int[]是因為eclipse的選擇
List<int[]> list = Arrays.asList(inta);
System.out.println("list的大小為:"+ list.size());
for (int[] is : list) {
System.out.println(is);
}
Integer[] intb = {, , ,};
List<Integer> list2 = Arrays.asList(intb);
System.out.println("list2的大小為: "+list2.size());
for (Integer integer : list2) {
System.out.println(integer);
}
}
}
---結果----
list的大小為:
[[email protected]
list2的大小為:
----asList的源碼----
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
我們可以發現,asList的參數是一個泛型,當是基本類型的數組的時候基本類型是無法作為泛型傳入的,此時傳入的是int這個數組,是以得到的大小是1,輸出元素也隻是輸出的這個數組本身,而不是數組元素。是以eclipse也會建議我們把泛型寫成int[]。
asList不可變
list2.add();
----結果----
Exception in thread "main" java.lang.UnsupportedOperationException
當我們在上述代碼的基礎上對list2進行添加元素的時候,意外發生了。此時你肯定覺得很奇怪,明明是傳回的一個new ArrayList呀,怎麼可能連最基本的操作都無法實作呢?問題就在于,它呀的就不是正派的!它是在Arrays類中的一個靜态内部類。
private static class ArrayList<E> extends AbstractList<E>
implements RandomAccess, java.io.Serializable
{
private static final long serialVersionUID = -L;
private final E[] a;
ArrayList(E[] array) {
a = Objects.requireNonNull(array);
}
@Override
public int size() {
return a.length;
}
@Override
public Object[] toArray() {
return Arrays.copyOf(a, a.length, Object[].class);
}
@Override
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
int size = size();
if (a.length < size)
return Arrays.copyOf(this.a, size,
(Class<? extends T[]>) a.getClass());
System.arraycopy(this.a, , a, , size);
if (a.length > size)
a[size] = null;
return a;
}
@Override
public E get(int index) {
return a[index];
}
@Override
public E set(int index, E element) {
E oldValue = a[index];
a[index] = element;
return oldValue;
}
@Override
public int indexOf(Object o) {
E[] a = this.a;
if (o == null) {
for (int i = ; i < a.length; i++)
if (a[i] == null)
return i;
} else {
for (int i = ; i < a.length; i++)
if (o.equals(a[i]))
return i;
}
return -;
}
@Override
public boolean contains(Object o) {
return indexOf(o) >= ;
}
@Override
public Spliterator<E> spliterator() {
return Spliterators.spliterator(a, Spliterator.ORDERED);
}
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
for (E e : a) {
action.accept(e);
}
}
@Override
public void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator);
E[] a = this.a;
for (int i = ; i < a.length; i++) {
a[i] = operator.apply(a[i]);
}
}
@Override
public void sort(Comparator<? super E> c) {
Arrays.sort(a, c);
}
@Override
public Iterator<E> iterator() {
return new ArrayItr<>(a);
}
}
它所提供的方法隻有這麼幾種,不提供增減!!!不是我們熟悉集合中的ArrayList!這一點要記住!
視圖
asList也是視圖的一種,啥意思呢?就是說,你對asList後的List進行的所有操作,不僅在List中會得到展現,在原來的裡面也會被修改
list2.set(, );
for (Integer integer : intb) {
System.out.println(integer);
}
----結果----
看,我list2裡面改了一個值,輸出intb的時候發現也改了把~
解決方案
如果你想修改但是不改動原來的數組的話,這裡提供一個較好的方案
ArrayList<Integer> copy = new ArrayList<>(Arrays.asList(intb));
copy.add();
System.out.println(copy);
for (Integer integer : intb) {
System.out.print(integer);
}