博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
再说Java集合,subList之于ArrayList
阅读量:4593 次
发布时间:2019-06-09

本文共 4712 字,大约阅读时间需要 15 分钟。

上一章说了,但还有一块儿内容没说到,那就是subList方法。先看一段代码

public static void testSubList() {    List
stringList = new ArrayList<>(); stringList.add("牛魔王"); stringList.add("蛟魔王"); stringList.add("鹏魔王"); stringList.add("狮驼王"); stringList.add("猕猴王"); stringList.add("禺贼王"); stringList.add("美猴王"); List
substrings = stringList.subList(3,5); System.out.println(substrings.toString()); System.out.println(substrings.size()); substrings.set(1, "猪八戒"); System.out.println(substrings.toString()); System.out.println(stringList.toString());}

看看执行结果如何?

[狮驼王, 猕猴王]2[狮驼王, 猪八戒][牛魔王, 蛟魔王, 鹏魔王, 狮驼王, 猪八戒, 禺贼王, 美猴王]

第一和第二的执行结果,非常容易理解,subList()方法作用就是截取集合stringList中一个范围内的元素。

第三和第四的执行结果都值得分析了,首先截取的字符串集合值为 [狮驼王, 猕猴王] ,但因为猕猴王在大雷音寺被美猴王打死了,我们用猪八戒来代替猕猴王;

因此我们通过substrings.set(1, "猪八戒"),将这个集合中第二个位置的值“猕猴王”设置为“猪八戒”,最终打印出来的结果也正是我们所预期的;但同时我们打印原集合stringList,发现其中的“猕猴王”也变成了“猪八戒”。这就比较奇怪了,两个问题:

1.我们操作的是截取后的集合,为什么原集合会变?

2.我们设置截取后某个位置(如第2个位置)的值,原集合改变的却不是对应位置的值?

一. subList原理初探

接下来我们带着问题寻找答案,我们看一下subList()的源码

/** * Returns a view of the portion of this list between the specified * {@code fromIndex}, inclusive, and {@code toIndex}, exclusive.  (If  * {@code fromIndex} and {@code toIndex} are equal, the returned list is  * empty.)  The returned list is backed by this list, so non-structural * changes in the returned list are reflected in this list, and vice-versa. * The returned list supports all of the optional list operations. * * 

This method eliminates the need for explicit range operations (of * the sort that commonly exist for arrays). Any operation that expects * a list can be used as a range operation by passing a subList view * instead of a whole list. For example, the following idiom * removes a range of elements from a list: *

 *      list.subList(from, to).clear(); * 
* Similar idioms may be constructed for {@link #indexOf(Object)} and * {@link #lastIndexOf(Object)}, and all of the algorithms in the * {@link Collections} class can be applied to a subList. * *

The semantics of the list returned by this method become undefined if * the backing list (i.e., this list) is structurally modified in * any way other than via the returned list. (Structural modifications are * those that change the size of this list, or otherwise perturb it in such * a fashion that iterations in progress may yield incorrect results.) * * @throws IndexOutOfBoundsException {@inheritDoc} * @throws IllegalArgumentException {@inheritDoc} */public List

subList(int fromIndex, int toIndex) { subListRangeCheck(fromIndex, toIndex, size); return new SubList(this, 0, fromIndex, toIndex);}

看注释,大概有以下几个意思

  1. 返回的是原集合在fromIndex和toIndex之间的元素的视图,虽然为视图,但支持集合的所有方法;
  2. 当fromIndex和toIndex相同时,返回空的视图;
  3. 任何对截取的视图的操作都会被原集合所取代;

看注释仅能知道我们例子最后的运行结果是正常的,但是对原理也还并不是特别清楚。我们继续看源码。

首先我们在例子中调用subList(3, 5)时,是new了一个SubList,这个SubList是ArrayList内部类,继承了AbstractList

private class SubList extends AbstractList
implements RandomAccess { private final AbstractList
parent; private final int parentOffset; private final int offset; int size; SubList(AbstractList
parent, int offset, int fromIndex, int toIndex) { this.parent = parent; this.parentOffset = fromIndex; this.offset = offset + fromIndex; this.size = toIndex - fromIndex; this.modCount = ArrayList.this.modCount; }}

从这个内部类的源码中,我们可以看到:

  1. SubList并没有像ArrayList一样定义Object[]来存放数据,而定义了一个变量parent来保存传递的原集合;
  2. 定义了一个offset用于保存进行偏移量,当对SubList修改时,就可以通过偏移量找到parent中对应的位置;
  3. 定义了size用来表示我们在parent集合中可见范围是多少;

1677914-20190629082706329-339604873.png

有了上面的说明,其实SubList的原理已经很清晰了,接下来,我们用SubList中常用的方法来印证一下。

二. add(E e)方法

substrings.add("九头蛇");System.out.println(substrings.toString());System.out.println(stringList.toString());

接着上面的例子,在substrings中添加“九头蛇”,按照规则,add()方法添加元素会在集合的最后,也就是说substrings的第3个位置(下标为2),对应parent原集合的位置下标就是2+3=5,会在stringList第六个位置插入“九头蛇”。看一下输出的结果

[狮驼王, 猪八戒, 九头蛇][牛魔王, 蛟魔王, 鹏魔王, 狮驼王, 猪八戒, 九头蛇, 禺贼王, 美猴王]

可以看到结果的确如此,那么我们在看一下add(E e),在SubList这个内部类里面并没有发现该方法,因此我去父类中找。

在AbstractList中找到了

public boolean add(E e) {    add(size(), e);    return true;}

接下来,我们在SubList中找到了实现方法

public void add(int index, E e) {    rangeCheckForAdd(index);    checkForComodification();    parent.add(parentOffset + index, e);    this.modCount = parent.modCount;    this.size++;}

很明显,源代码和我们开始的分析是一致的,当然在添加之间需要进行空间容量判断,是否足以添加新的元素,扩容规则,我们上一章已经讲过。

三. 其他方法

关于SubList的其他方法,其实和add原理一样,不论是set(int index, E e),get(int index),addAll(Collection<? extends E> c),remove(int index),都是先判断当前传入的位置索引是否正确(如是否大于size,小于0等),再根据规则计算出原集合中的位置下标,最终完成对集合的操作。

四. 总结

本文续接上一章ArrayList原理及使用,对ArrayList中的常用方法subList进行了剖析,从源码的角度对通过subList方法得到的集合和原集合有何关系,有何不同点,从而避免工作中遇到各种坑,若有不对之处,请批评指正,望共同进步,谢谢!

转载于:https://www.cnblogs.com/LiaHon/p/11105573.html

你可能感兴趣的文章
读书笔记:JavaScript编程全解
查看>>
大小端格式
查看>>
阅读书籍电技术
查看>>
互联网时代的报纸收费与读者细分
查看>>
mysql优化
查看>>
vs2012中怎样设为起始页,怎样取消
查看>>
CSS3中的box-shadow
查看>>
Java常用函数式接口--Supplier接口使用案例
查看>>
【常识】常见外国计量单位
查看>>
MySQL索引
查看>>
新版本读取老版本文件崩溃BUG
查看>>
高可用Hadoop平台-应用JAR部署
查看>>
【随感】不要以为自己不足轻重而放任自己做一些事或一些话。你的不在意,才会影响到别人也不在意你。...
查看>>
集美大学网络15软工个人作业4分数统计
查看>>
奇怪吸引子---四涡卷超混沌吸引子
查看>>
leetcode 437. 路径总和 III
查看>>
ued.taobao.com
查看>>
香港身份证
查看>>
(二)Python selenium
查看>>
7.装饰器的一些需求
查看>>