java集合类源码学习二
我们查看Collection接口的hierarchy时候,可以看到AbstractCollection<E>这样一个抽象类,它实现了Collection接口的部分方法,Collection集合系列的各个集合类都继承于该抽象类。我们来看看这个类:
public abstract class AbstractCollection<E> implements Collection<E> {
protected AbstractCollection() {
}
public abstract Iterator<E> iterator();
public abstract int size();
public boolean isEmpty() {
return size() == 0;
}
public boolean contains(Object o) {
Iterator<E> it = iterator();
if (o==null) {
while (it.hasNext())
if (it.next()==null)
return true;
} else {
while (it.hasNext())
if (o.equals(it.next()))
return true;
}
return false;
}
public Object[] toArray() {
// Estimate size of array; be prepared to see more or fewer elements
Object[] r = new Object[size()];
Iterator<E> it = iterator();
for (int i = 0; i < r.length; i++) {
if (! it.hasNext()) // fewer elements than expected
return Arrays.copyOf(r, i);
r[i] = it.next();
}
return it.hasNext() ? finishToArray(r, it) : r;
}
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
// Estimate size of array; be prepared to see more or fewer elements
int size = size();
T[] r = a.length >= size ? a :
(T[])java.lang.reflect.Array
.newInstance(a.getClass().getComponentType(), size);
Iterator<E> it = iterator();
for (int i = 0; i < r.length; i++) {
if (! it.hasNext()) { // fewer elements than expected
if (a == r) {
r[i] = null; // null-terminate
} else if (a.length < i) {
return Arrays.copyOf(r, i);
} else {
System.arraycopy(r, 0, a, 0, i);
if (a.length > i) {
a[i] = null;
}
}
return a;
}
r[i] = (T)it.next();
}
// more elements than expected
return it.hasNext() ? finishToArray(r, it) : r;
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
@SuppressWarnings("unchecked")
private static <T> T[] finishToArray(T[] r, Iterator<?> it) {
int i = r.length;
while (it.hasNext()) {
int cap = r.length;
if (i == cap) {
int newCap = cap + (cap >> 1) + 1;
// overflow-conscious code
if (newCap - MAX_ARRAY_SIZE > 0)
newCap = hugeCapacity(cap + 1);
r = Arrays.copyOf(r, newCap);
}
r[i++] = (T)it.next();
}
// trim if overallocated
return (i == r.length) ? r : Arrays.copyOf(r, i);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError
("Required array size too large");
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
public boolean add(E e) {
throw new UnsupportedOperationException();
}
public boolean remove(Object o) {
Iterator<E> it = iterator();
if (o==null) {
while (it.hasNext()) {
if (it.next()==null) {
it.remove();
return true;
}
}
} else {
while (it.hasNext()) {
if (o.equals(it.next())) {
it.remove();
return true;
}
}
}
return false;
}
public boolean containsAll(Collection<?> c) {
for (Object e : c)
if (!contains(e))
return false;
return true;
}
public boolean addAll(Collection<? extends E> c) {
boolean modified = false;
for (E e : c)
if (add(e))
modified = true;
return modified;
}
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
boolean modified = false;
Iterator<?> it = iterator();
while (it.hasNext()) {
if (c.contains(it.next())) {
it.remove();
modified = true;
}
}
return modified;
}
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
boolean modified = false;
Iterator<E> it = iterator();
while (it.hasNext()) {
if (!c.contains(it.next())) {
it.remove();
modified = true;
}
}
return modified;
}
public String toString() {
Iterator<E> it = iterator();
if (! it.hasNext())
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
E e = it.next();
sb.append(e == this ? "(this Collection)" : e);
if (! it.hasNext())
return sb.append(']').toString();
sb.append(',').append(' ');
}
}
}
从它的代码里我们可以看到,它有一个最大长度,是Integer的最大值减去8,至于为什么减8,是因为有些JVM会在数组里保存几个关键字符。我们再看这个contains方法,我很好奇的是,既然参数是Object类型,那么就包括了null,为什么判断的时候还要把null单独拿出来,这个疑问不知哪个大佬能给我解释一下。
再看toArray的不带参方法,new了一个和size()方法表示的长度一样长的Object数组r,还弄了一个迭代器出来,然后用for来循环r数组的每一个下标,并在每一次循环中判断迭代器的hasNext()是否为false,如果为false则说明当前集合长度比r数组短,则用Array的copyOf()方法把r数组放到一个长度为i的数组里,这样就不浪费多余的空间了。最后判断如果hasNext()还为true,则说明当前集合长度比r数组长,就用finishToArray()方法返回一个Object数组。至于为什么比较过和size()的大小之后还有这种操作,上面注释有说明,是迭代的时候假如对元素有增删操作,会导致长度有变化。finishToArray(T[] r, Iterator<?> it)方法把数组r扩容之后往里面添加迭代器后续元素,可以看到hugeCapacity这个玩意,当数组长度超过Integer最大值减8的时候,就用Integer的最大值,这样数组的长度是不能超过2^31的,毕竟数组下标使用int数字表示的。带参的toArray方法差不多意思,是可以将集合转化成指定类型的数组。
add()方法会直接抛出异常,说明AbstractCollection是不允许添加元素的,那么一定是在具体实现类里实现的这个方法。至于其他的方法,看一下就明白了。
接着我们来看看public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>,这是list的抽象类,继承自AbstractCollection,实现List接口的部分方法。源码就不贴了,现在重点来看一下内部类Itr,这是一个迭代器的实现类。里面定义了三个变量:游标cursor、最近一次调用返回的索引lastRet和期望得到的修改次数expectedModCount。里面实现了迭代器的三个方法,注意next()和remove()方法里都调用了这么一个方法checkForComodification(),这个用来比较expectedModCount和modCount,如果不同就抛出异常ConcurrentModificationException,这个意思是如果在迭代操作的时候,如果有用非迭代器的操作对集合做了修改,那么就会抛出异常。对于modCount,我们可以在AbstractList的子类中看到,每次对集合做了操作,modCount就会加一。
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
list.add(11);
list.add(12);
list.add(13);
list.add(14);
list.add(15);
Iterator<Integer> itr = list.iterator();
while(itr.hasNext()) {
if(itr.next() == 13) {
itr.remove();
}
}
System.out.println(list);
}
在迭代的时候用迭代器的remove操作就不会报错,如果在增强for循环中,用了ArrayList自带的remove,就会报异常了。下面的内部类ListItr是一个ListIterator的实现类,可以往前往后迭代,还有多了几个方法,代码都很简单。subList()截取一段子序列,支持往前往后截取。下面还有个SubList类,里面有个往后截取一段子序列的方法,其他的起始都是用的父类的方法。
java集合类源码学习二的更多相关文章
- java集合类源码学习一
对于java的集合类,首先看张图 这张图大致描绘出了java集合类的总览,两个体系,一个Collection集合体系一个Map集合体系.在说集合类之前,先说说Iterable这个接口,这个接口在jdk ...
- java集合类源码学习三——ArrayList
ArrayList无疑是java集合类中的一个巨头,而且或许是使用最多的集合类.ArrayList继承自AbstractList抽象类,实现了List<E>, RandomAccess, ...
- Java集合类源码学习- Iterabel<T>,Colection<E>,AbstractCollection<E>
Collection<E>接口extends Iteratable<E>接口. Iteratable<T>:实现接口使得对象能够成为“for-each loop”的 ...
- Java集合类源码解析:Vector
[学习笔记]转载 Java集合类源码解析:Vector 引言 之前的文章我们学习了一个集合类 ArrayList,今天讲它的一个兄弟 Vector.为什么说是它兄弟呢?因为从容器的构造来说,Vec ...
- Dubbo源码学习(二)
@Adaptive注解 在上一篇ExtensionLoader的博客中记录了,有两种扩展点,一种是普通的扩展实现,另一种就是自适应的扩展点,即@Adaptive注解的实现类. @Documented ...
- Java集合类源码解析:HashMap (基于JDK1.8)
目录 前言 HashMap的数据结构 深入源码 两个参数 成员变量 四个构造方法 插入数据的方法:put() 哈希函数:hash() 动态扩容:resize() 节点树化.红黑树的拆分 节点树化 红黑 ...
- java Integer 源码学习
转载自http://www.hollischuang.com/archives/1058 Integer 类在对象中包装了一个基本类型 int 的值.Integer 类型的对象包含一个 int 类型的 ...
- Java集合源码学习(一)集合框架概览
>>集合框架 Java集合框架包含了大部分Java开发中用到的数据结构,主要包括List列表.Set集合.Map映射.迭代器(Iterator.Enumeration).工具类(Array ...
- python 协程库gevent学习--gevent源码学习(二)
在进行gevent源码学习一分析之后,我还对两个比较核心的问题抱有疑问: 1. gevent.Greenlet.join()以及他的list版本joinall()的原理和使用. 2. 关于在使用mon ...
随机推荐
- ReentrantLock与synchronized 源码解析
一.概念及执行原理 在 JDK 1.5 之前共享对象的协调机制只有 synchronized 和 volatile,在 JDK 1.5 中增加了新的机制 ReentrantLock,该机制的诞生并 ...
- 2020-05-24:ZK分布式锁有几种实现方式?各自的优缺点是什么?
福哥答案2020-05-24: Zk分布式锁有两种实现方式一种比较简单,应对并发量不是很大的情况.获得锁:创建一个临时节点,比如/lock,如果成功获得锁,如果失败没获得锁,返回false释放锁:删除 ...
- C#LeetCode刷题之#459-重复的子字符串(Repeated Substring Pattern)
问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3945 访问. 给定一个非空的字符串,判断它是否可以由它的一个子串 ...
- C#LeetCode刷题之#231-2的幂(Power of Two)
问题 该文章的最新版本已迁移至个人博客[比特飞],单击链接 https://www.byteflying.com/archives/3858 访问. 给定一个整数,编写一个函数来判断它是否是 2 的幂 ...
- 精灵小巧的 Jsonpath 万精油:Snack3
前几天和一个群里的朋友交流一个需求:在 Json 里像 XPath 一样找出节点,并修改值,然后输出新的Json. 很多人马上就会说,Jsonpath 啊. 是的 Jsonpath 确实能快速找出节点 ...
- 洛谷P1036.选数(DFS)
题目描述 已知 n个整数 x1,x2,-,xn,以及11个整数k(k<n).从n个整数中任选k个整数相加,可分别得到一系列的和.例如当n=4,k=3,4个整数分别为3,7,12,19时,可得全部 ...
- vue-cli 安装教程(转)
vue-cli这个构建工具大大降低了webpack的使用难度,支持热更新,有webpack-dev-server的支持,相当于启动了一个请求服务器,给你搭建了一个测试环境,只关注开发就OK. 1.安装 ...
- 浅谈Docker(二)
Docker使用cgroup实现CPU,内存和磁盘IO等系统资源的限制. CPU Docker现在有2个与CPU资源相关的参数,-c可以指定CPU的占比,--cpuset可以绑定CPU.例如,指定容器 ...
- 新建一个Vue项目
node环境以及vue的安装可查看:https://www.cnblogs.com/renlywen/p/13522869.html 第一步:创建项目 终端输入: vue init webpack d ...
- Linux离线安装mysql 5.6详细步骤
一.安装MySQL 1.下载安装包 mysql-5.6.40-linux-glibc2.12-x86_64.tar.gz 2.卸载系统自带的Mariadb rpm -qa|grep mariadb / ...