天天看點

新手讀源碼__subList和AsList使用細節,視圖的底層應用

前言

作為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);
}