前言
作为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);
}