Java源码解析之接口List

网友投稿 222 2023-01-14

Java源码解析之接口List

前言

List接口是Collection接口的三大接口之一,其中的数据可以通过位置检索,用户可以在指定位置插入数据。List的数据可以为空,可以重复。我们来看看api文档是怎么说的:

一、List特有的方法

我们这里就只关注和Collection不同的方法,主要有以下这些:

//在指定位置,将指定的集合插入到当前的集合中

boolean addAll(int index, Collection<? extends E> c);

//这是一个默认实现的方法,会通过Iterator的方式对每个元素进行指定的操作

default void replaceAll(UnaryOperator operator) {

Objects.requireNonNull(operator);

final ListIterator li = this.listIterator();

while (li.hasNext()) {

li.set(operator.apply(li.next()));

}

}

//排序,依据指定的规则对当前集合进行排序,可以看到,排序是通过Arrays这个工具类完成的。

default void sort(Comparator super E> c) {

Object[] a = this.toArray();

Arrays.sort(a, (Comparator) c);

ListIterator i = this.listIterator();

for (Object e : a) {

i.next();

i.set((E) e);

}

}

//获取指定位置的元素

E get(int index);

//修改指定位置元素的值

E set(int index, E element);

//将指定元素添加到指定的位置

void add(int index, E element);

//将指定位置的元素移除

E remove(int index);

//返回一个元素在集合中首次出现的位置

int indexOfhttp://(Object o);

//返回一个元素在集合中最后一次出现的位置

int lastIndexOf(Object o);

//ListIterator继承于Iterator,主要增加了向前遍历的功能

ListIterator listIterator();

//从指定位置开始,返回一个ListIterator

ListIterator listIterator(int index);

//返回一个子集合[fromIndex, toIndex),非结构性的修改返回值会反映到原表,反之亦然。

//如果原表进行了结构修改,则返回的子列表可能发生不可预料的事情

List subList(int fromIndex, int toIndex);

通过对上面方法的研究,我们不难发现,collection接口主要提供一些通常的方法,而List接口则针对线性表的结构,提供了对位置以及字表的操作。

二、超级实现类AbstractList

我们先看看源文档是怎么来说AbstractList的,要实现一个不可修改的集合,只需要复写get和size方法。如果要实现一个可以修改的集合,还需要复写set方法,如果要动态调整大小,就必须实现add和remove方法。

接下里我们一起来来看看源码吧!

//在AbstractCollection中,add方法默认会抛出异常,

//而在这里是调用了add(int index, E e)方法,但这个方法也是没有实现的。

//这里默认会把元素添加到末尾。

public boolean add(E e) {

add(size(), e);

return true;

}

//同上,这个只需要进行一次遍历即可

public boolean addAll(int index, Collection extends E> c) {

//...

}

接下里我们在继续看看其他几个方法,这几个是与Iterator和ListIterator息息相关的,在AbstractList中有具体的实现,我们先来看看它是如何把集合转变成Iterator对象并支持foreach循环的吧。

我们通过源码发现:在Iterator方法中,是直接返回一个 Itr对象

public Iterator iterator() {

return new Itr();

}

其实我们很快也就会明白,它是实现了一个内部类,这个内部类实现了Iterator接口,合理的处理hasNext、next、remove方法。这个源码就不粘贴啦,其中仅仅在remove时考虑了一下多线程问题,有兴趣的可以自己去看看。

我们来看看另一个吧–ListIterator吧他也是通过一个内部类是实现的

public ListIterator listIterator() {

return listIterator(0);

}

public ListIterator listIterator(final int index) {

rangeCheckForAdd(index);

return new ListItr(index);

}

事实证明,和我们想的一样,AbstractList内部还定义了一个ListItr,实现了ListIterator接口,其实现也很简单,就不粘贴源码啦。

接下俩让我们来看看AbtractList是怎么利用这两个类来做事情的

//寻找一个元素首次出现的位置,只需要从前往后遍历,找到那个元素并返回其位置即可。

public int indexOf(Object o) {

ListIterator it = listIterator();

if (o==null) {

while (it.hasNext())

if (it.next()==null)

return it.previousIndex();

} else {

while (it.hasNext())

if (o.equals(it.next()))

return it.previousIndex();

}

return -1;

}

//同理,寻找一个元素最后一次出现的位置,只需要从列表最后一位向前遍历即可。

//看到listIterator(int index)方法是可以传递参数的,这个我想我们都可以照着写出来了。

public int lastIndexOf(Object o) {

//...

}

//这个方法是把从fromIndex到toIndex之间的元素从集合中删除。

//clear()方法也是调用这个实现的(我认为clear实现意义并不大,因为在其上级AbstractCollection中已经有了具体实现)。

protected void removeRange(int fromIndex, int toIndex) {

ListIterator it = listIterator(fromIndex);

for (int i=0, n=toIndex-fromIndex; i

it.next();

it.remove();

}

}

在接下来让我们来说一说两个比较重要的内容一个是关于SubList,另一个是关于equals和hascode的。

三、SubList、equals和hascode

SubList并不是新建了一个list,只是持有当前集合的引用,然后控制了用户可以操作的范围,所以在接口定义时就说明了其更改会直接反应到原集合中。SubList是定AbstractList内部,并且是AbstractList的基础上增加了对可选范围的控制。

而equals和hascode的实现,也关乎我们的使用。在AbstractList中,这两个方法不仅与其实例有关,也和其内部包含的元素有关,所以在定义数据元素时,也应该复写这两个方法,以保证程序的正确运行。这里看下其源码加深一下印象吧。

public boolean equals(Object o) {

if (o == this)

return true;

if (!(o instanceof List))

return false;

ListIterator e1 = listIterator();

ListIterator> e2 = ((List>) o).listIterator();

while (e1.hasNext() && e2.hasNext()) {

E o1 = e1.next();

Object o2 = e2.next();

//这里用到了数据元素的equals方法

if (!(o1==null ? o2==null : o1.equals(o2)))

return false;

}

return !(e1.hasNext() || e2.hasNext());

}

public int hashCode() {

int hashCode = 1;

for (E e : this)

//这里用到了数据元素的hashCode方法

hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());

return hashCode;

}

it.next();

it.remove();

}

}

在接下来让我们来说一说两个比较重要的内容一个是关于SubList,另一个是关于equals和hascode的。

三、SubList、equals和hascode

SubList并不是新建了一个list,只是持有当前集合的引用,然后控制了用户可以操作的范围,所以在接口定义时就说明了其更改会直接反应到原集合中。SubList是定AbstractList内部,并且是AbstractList的基础上增加了对可选范围的控制。

而equals和hascode的实现,也关乎我们的使用。在AbstractList中,这两个方法不仅与其实例有关,也和其内部包含的元素有关,所以在定义数据元素时,也应该复写这两个方法,以保证程序的正确运行。这里看下其源码加深一下印象吧。

public boolean equals(Object o) {

if (o == this)

return true;

if (!(o instanceof List))

return false;

ListIterator e1 = listIterator();

ListIterator> e2 = ((List>) o).listIterator();

while (e1.hasNext() && e2.hasNext()) {

E o1 = e1.next();

Object o2 = e2.next();

//这里用到了数据元素的equals方法

if (!(o1==null ? o2==null : o1.equals(o2)))

return false;

}

return !(e1.hasNext() || e2.hasNext());

}

public int hashCode() {

int hashCode = 1;

for (E e : this)

//这里用到了数据元素的hashCode方法

hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());

return hashCode;

}

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:网易新闻免费api(网易新闻免费下载安装到桌面)
下一篇:中幸物流查询(中物北京物流信息服务有限公司)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~