新手读源码__subList和AsList使用细节,视图的底层应用

前言

作为Java集合部分最后一小块内容,我这里决定谈谈两个方法subList和asList方法,书上对这两个方法的讲解不多,有几点是需要我们注意的

subList

功能:subList和subString的功能一样,都是用来切割用的。但是它们两者还是有微小的区别的。让我们通过实例来看看

视图

public class SubListTest {

    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);

        List<Integer> list2 = new ArrayList<>(list);
        List<Integer> list3 = list2.subList(0, list2.size());

        System.out.println(list2);
        // 证明subList返回的是新对象
        System.out.println(list2 == list3);
        list3.add(3);
        System.out.println("list2: "+list2);
        System.out.println("list3: "+list3);

    }
}

----结果----
[1, 2]
false
list2: [1, 2, 3]
list3: [1, 2, 3]

解读一下:

通过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(1);
}

我们可以从源码中得出结论,所有的操作其实最终都直接在原表中直接进行操作的!它就像一个代理。将对自己的操作全部发送到原表上。

modCount锁定原表

生成子列表之后,不能再动原表

public class SubListTest {

    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);

        List<Integer> list3 = list.subList(0, list.size());
        list.add(3);
        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 = {1, 2, 3, 4};
        // 泛型写成int[]是因为eclipse的选择
        List<int[]> list = Arrays.asList(inta);
        System.out.println("list的大小为:"+ list.size());
        for (int[] is : list) {
            System.out.println(is);
        }

        Integer[] intb = {1, 2, 3 ,4};
        List<Integer> list2 = Arrays.asList(intb);
        System.out.println("list2的大小为: "+list2.size());
        for (Integer integer : list2) {
            System.out.println(integer);
        }
    }
}

---结果----
list的大小为:1
[I@6073f712
list2的大小为: 4
1
2
3
4

----asList的源码----
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}

我们可以发现,asList的参数是一个泛型,当是基本类型的数组的时候基本类型是无法作为泛型传入的,此时传入的是int这个数组,所以得到的大小是1,输出元素也只是输出的这个数组本身,而不是数组元素。所以eclipse也会建议我们把泛型写成int[]。

asList不可变

list2.add(1);

----结果----
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 = -2764017481108945198L;
        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, 0, a, 0, 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 = 0; i < a.length; i++)
                    if (a[i] == null)
                        return i;
            } else {
                for (int i = 0; i < a.length; i++)
                    if (o.equals(a[i]))
                        return i;
            }
            return -1;
        }

        @Override
        public boolean contains(Object o) {
            return indexOf(o) >= 0;
        }

        @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 = 0; 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(0, 2);
for (Integer integer : intb) {
    System.out.println(integer);
}

----结果----
2
2
3
4

看,我list2里面改了一个值,输出intb的时候发现也改了把~

解决方案

如果你想修改但是不改动原来的数组的话,这里提供一个较好的方案

ArrayList<Integer> copy = new ArrayList<>(Arrays.asList(intb));
copy.add(5);
System.out.println(copy);
for (Integer integer : intb) {
    System.out.print(integer);
}

猜你喜欢

转载自blog.csdn.net/qq_41376740/article/details/80374817