ArrayList 源码分析 (顺便复习序列化,单例)
2016-10-15 19:05
351 查看
一、Something about ArrayList
- 每次添加超过限制,列表就会增加50%容量,每次扩容挺浪费时间的,如果一开始就知道大概的列表长度,可以直接构造
- 采用System.arrayCopy()复制到新数组
- 列表可以按数组下标访问元素-get(i)/set(i)
- remove时,需要复制移动元素,性能很差
- 线程不安全,在多线程环境下可以考虑用Collections.synchronizedList(List l)函数返回一个线程安全的ArrayList类~
ArrayList实现了,
1. java.io.Serializable接口,支持序列化
复习1:
每个枚举类型都会继承类java.lang.Enum,而该类实现了Serializable接口,所以所有类型对象都是默认可以被序列化的。当对象C被持久化到一个A地文件中时,必须确保B地的classpath中包含C.class才可以从A地还原对象。
因为序列化是持久化对象的状态,所以会自动忽略静态数据。如果有不想序列化的数据域时,可以采用关键字transient来修饰,也可以使用transient数据再次被序列化-重写writeObject和readObject:
假设两个对象同时引用同一个对象,我们不能只存储内存引用地址,因为当对象重新被加载中,可能与原来的内存地址完全不同,所以被引用的对象也将被序列化。序列化时,每个对象都有一个序列号,第一次遇到的时候,将该对象保存到流中,再次遇到,只会写入类似“与序列号X对象相同”,读取的时候也一样,第一次遇到去构建,再次遇到的之后直接获取相关的对象引用!
使用Externalizable接口之后,之前基于Serializable接口的序列化机制就将失效~序列化的细节需要程序员去完成,如果writeExternal()和readExternal()未做处理,序列化行为将不会保存或者读取任何一起字段。读取对象时,会调用被序列化类的无参构造器去创建一个新的对象,然后再将保存对象的字段的值填充到新的对象中~所以实现Externalizable接口的类必须要提供一个无参构造器,并且它的访问权限必须为public~
在单例模式中,我们期望某个类的实例始终唯一,但是采用序列化会返回一个新的对象,该怎么办呢?此时需要定义readResolve方法来代替,返回唯一的单例对象,对象序列化之后就可以直接调用它~但是无论是Serializable或者Externalizable接口,都可以定义readResolve~
protected Object readResolve() throws ObjectStreamException
指纹是一个根据类名,接口名,成员方法以及属性等来生成一个64位的哈希字段。可以利用JDK的bin目录下的serialver.exe工具产生这个serialVersionUID的值,命令为“serialver 类名”。类如果发生变化,它的SHA指纹也会跟着变化。除此之外,同一个类在不同的JVM上生成的SHA可能会不一样,这样在反序列化的过程中就会出现问题,因为如果SHA不一样的话,会抛出异常。为了实现一个类对其早期版本的兼容,可以将指纹定义到一个静态常量serialVersionUID里面。
当指纹一致,假如类的方法发生了改变,则读入新对象数据的时候不会发生任何问题。假如流中的对象具有在当前版本中所没有的数据域,那么会忽略这些额外的数据,相反的如果当前版本中有流中的对象没有的数据域,则会将这些新添加的域设置为它们的默认值。但是如果两部分数据之间名字匹配而类型不匹配的话就不会兼容~
复习2:
单例模式7种写法:
1)懒汉
- 线程不安全
2)饿汉
3)静态内部类
4)双重校验锁
是懒汉方式的升级版~避免synchronized带来的效率问题~
1. 在内存中为对象分配空间
2. 初始化对象
3. 将变量指向刚分配的内存地址
根据JAVA语言规范,所有线程在执行程序时要遵守intra-thread semantics: 可以运行那些在单线程内不会改变单线程程序执行结果的重排序。所以:
在多线程环境下,这种重排序就会导致问题发生:实例可能还没有初始化,此时另一个线程调用getInstance,就会取到状态不正确的对象,程序就会出错~而volatile的一个语义是禁止指令重排序优化~
- 其他方法可以使用反射强行调用私有构造器
应用场景:
> 线程池
> 网站计数器
> 数据库连接池
> 任务管理器
公共访问节点,生成唯一标识等,比如线程池,网站计数器,数据库连接池:
http://blog.csdn.net/likika2012/article/details/11483167
2. RandomAccess接口,支持快速随机访问
3. Clonable接口,支持被克隆
修改
懒惰拷贝:浅拷贝 ---> 深拷贝
可以通过序列化来实现深拷贝~
三者都是标识接口,没有任何方法和属性,当一个类实现了一个标识接口之后就像是给自己打了个标签:
1. 属性
为什么elementData是transient?
因为elementData是一个缓存数组,所以有些空间并没有元素,都序列化的话,太浪费空间了~
6260652: http://www.tuicool.com/articles/uIBB3q,http://blog.csdn.net/u014082714/article/details/51811998
Arrays有一个内部类ArrayList(不支持add,remove等改变list方法),asList会返回该ArrayList,而不是java.util.ArrayList~
3. 元素存储
- 如果是空实例{},就采用默认的容量10
- 判断minCapacity是否overflow(大于Integer.MAX_VALUE的话会溢出为负)或是否大于数组长度(minCapacity = size + n, minCapacity - elementData.length?>0)
1)newCapacity = 1.5*oldCapacity
2)如果newCapacity溢出或者小于minCapacity(要保证最小空间大小)的情况下,newCapacity重新取值minCapacity
3)如果newCapacity大于MAX_ARRAY_SIZE,就判断minCapacity是否大于MAX_ARRAY_SIZE,大于就取Integer.MAX_VALUE,否则就取MAX_ARRAY_SIZE~(继续用newCapacity很容易就溢出,但是实际上很多空间都还没装满元素,用minCapacity即满足空间要求,又不会太浪费)
4. 删除元素
removeRange为protected方法,所以继承该类才能被使用,一般使用list.subList(start, end).clear();来代替removeRange,而clear实质也是调用SubList里面的removeRange方法。
快速失败机制:依赖于modcount,在面对并发的修改时,迭代器很快就会完全失败,而不是冒着将来某个不确定时间发生任意不确定行为的风险
Reference:
http://blog.csdn.net/li295214001/article/details/48135939
http://blog.jobbole.com/94074/
http://blog.csdn.net/goodlixueyong/article/details/51935526
http://www.blogjava.net/kenzhh/archive/2013/03/15/357824.html
http://blog.csdn.net/likika2012/article/details/11483167
http://developer.51cto.com/art/201202/317181.htm
http://stackoverflow.com/questions/2289183/why-is-javas-abstractlists-removerange-method-protected
http://blog.csdn.net/moreevan/article/details/6698529
http://www.cnblogs.com/ITtangtang/p/3948555.html
http://yikun.github.io/2015/04/04/Java-ArrayList%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E5%8F%8A%E5%AE%9E%E7%8E%B0/
- 每次添加超过限制,列表就会增加50%容量,每次扩容挺浪费时间的,如果一开始就知道大概的列表长度,可以直接构造
- 采用System.arrayCopy()复制到新数组
- 列表可以按数组下标访问元素-get(i)/set(i)
- remove时,需要复制移动元素,性能很差
- 线程不安全,在多线程环境下可以考虑用Collections.synchronizedList(List l)函数返回一个线程安全的ArrayList类~
ArrayList实现了,
1. java.io.Serializable接口,支持序列化
复习1:
每个枚举类型都会继承类java.lang.Enum,而该类实现了Serializable接口,所以所有类型对象都是默认可以被序列化的。当对象C被持久化到一个A地文件中时,必须确保B地的classpath中包含C.class才可以从A地还原对象。
因为序列化是持久化对象的状态,所以会自动忽略静态数据。如果有不想序列化的数据域时,可以采用关键字transient来修饰,也可以使用transient数据再次被序列化-重写writeObject和readObject:
transient private Integer age = null; private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeInt(age); } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); age = in.readInt(); }readObject和writeObject都是private方法,而且不存在Object,Serializable,对象流定义中,所以它们是如何被对象流调用的呢?反射~
假设两个对象同时引用同一个对象,我们不能只存储内存引用地址,因为当对象重新被加载中,可能与原来的内存地址完全不同,所以被引用的对象也将被序列化。序列化时,每个对象都有一个序列号,第一次遇到的时候,将该对象保存到流中,再次遇到,只会写入类似“与序列号X对象相同”,读取的时候也一样,第一次遇到去构建,再次遇到的之后直接获取相关的对象引用!
package kevin.seria; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class Simulator { public static void main(String[] args) { new Simulator().go(); } private void go(){ try { ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("seria")); Student student1 = new Student(new NewBook(2011,"moree"),"kevin"); out.writeObject(student1); // student1.setName("Jordan"); out.writeObject(student1); student1.setName("Paul"); out.writeObject(student1); System.out.println("object has been written.."); out.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } try{ ObjectInputStream in = new ObjectInputStream(new FileInputStream("seria")); Student s1 = (Student)in.readObject(); Student s2 = (Student)in.readObject(); Student s3 = (Student)in.readObject(); System.out.println("Objects read here: "); System.out.println("Student1's name: "+s1.getName()); System.out.println("Student2's name: "+s2.getName()); System.out.println("Student3's name: "+s3.getName()); }catch(FileNotFoundException e){ e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }一个基类实现了序列化接口,那么它的子类都可以序列化。父类没有实现序列化接口,而子类实现了该接口的时候,反序列化的时候会调用父类的无参构造函数,如果父类没有无参构造函数会抛出异常~
使用Externalizable接口之后,之前基于Serializable接口的序列化机制就将失效~序列化的细节需要程序员去完成,如果writeExternal()和readExternal()未做处理,序列化行为将不会保存或者读取任何一起字段。读取对象时,会调用被序列化类的无参构造器去创建一个新的对象,然后再将保存对象的字段的值填充到新的对象中~所以实现Externalizable接口的类必须要提供一个无参构造器,并且它的访问权限必须为public~
private String name = "Andy"; transient private Integer age = 12; public void writeExternal(ObjectOutput out) throws IOExeception { out.writeObject(name); out.writeInt(age); } public void writeExternal(ObjectInput in) throws IOException, ClassNotFoundException { name = (String) in.readObject(); age = in.readInt(); }
在单例模式中,我们期望某个类的实例始终唯一,但是采用序列化会返回一个新的对象,该怎么办呢?此时需要定义readResolve方法来代替,返回唯一的单例对象,对象序列化之后就可以直接调用它~但是无论是Serializable或者Externalizable接口,都可以定义readResolve~
protected Object readResolve() throws ObjectStreamException
指纹是一个根据类名,接口名,成员方法以及属性等来生成一个64位的哈希字段。可以利用JDK的bin目录下的serialver.exe工具产生这个serialVersionUID的值,命令为“serialver 类名”。类如果发生变化,它的SHA指纹也会跟着变化。除此之外,同一个类在不同的JVM上生成的SHA可能会不一样,这样在反序列化的过程中就会出现问题,因为如果SHA不一样的话,会抛出异常。为了实现一个类对其早期版本的兼容,可以将指纹定义到一个静态常量serialVersionUID里面。
当指纹一致,假如类的方法发生了改变,则读入新对象数据的时候不会发生任何问题。假如流中的对象具有在当前版本中所没有的数据域,那么会忽略这些额外的数据,相反的如果当前版本中有流中的对象没有的数据域,则会将这些新添加的域设置为它们的默认值。但是如果两部分数据之间名字匹配而类型不匹配的话就不会兼容~
复习2:
单例模式7种写法:
1)懒汉
- 线程不安全
public class Singleton { private static Singleton s = null; private Singleton() {} public static Singleton getInstance() { if(s == null) s = new Singleton(); return s; } }- 线程安全
public static sysnchronized Singleton getInstance()效率不高,每次访问都需要等待另一个线程访问完毕才可以进入~
2)饿汉
public class Singleton { private static Singleton s = new Singleton(); private Singleton() {} public static Singleton getInstance() { return s; } }类加载的时候就创建单例,并存放在内存中,如果单例占用的内存空间比较大,那饿汉方式就不大合适了~
3)静态内部类
public class Singleton { private static class SingletonHolder { private static final Singleton s = new Singleton(); } private Singleton() {} public static Singleton getInstance() { return SingletonHolder. s; } }当调用getInstance方法的时候才去加载SinglerHolder~实现lazy loading~
4)双重校验锁
是懒汉方式的升级版~避免synchronized带来的效率问题~
public class Singleton { private static Singleton s = null; private Singleton() {} public static Singleton getInstance() { if(s == null) synchronized(Singleton. class) { if( s == null) s = new Singleton(); } return s; } }如果instance不为null,那么不需要执行加锁和初始化操作,减少了synchronized带来的开销,但是,s = new Singleton()会带来风险,这句话可以分解为:
1. 在内存中为对象分配空间
2. 初始化对象
3. 将变量指向刚分配的内存地址
根据JAVA语言规范,所有线程在执行程序时要遵守intra-thread semantics: 可以运行那些在单线程内不会改变单线程程序执行结果的重排序。所以:
在多线程环境下,这种重排序就会导致问题发生:实例可能还没有初始化,此时另一个线程调用getInstance,就会取到状态不正确的对象,程序就会出错~而volatile的一个语义是禁止指令重排序优化~
private volatile static Singleton s = null;5)枚举(Effective java推荐)
public enum Singleton { INSTANCE; public void relevantMethod() {} }- 不需要像其他方法一样需要额外的工作来实现序列化
- 其他方法可以使用反射强行调用私有构造器
public enum Season { SPRING, SUMMER, AUTUMN, WINTER; }
public final class T extends Enum { //不能继承 public static final T SPRING; public static final T SUMMER; public static final T AUTUMN; public static final T WINTER; private static final T ENUM$VALUES[]; private T(String name, int i) { super(name, i); } static{ SPRING = new T("SPRING, 0"); SUMMER = new T("SUMMER, 1"); AUTUMN = new T("AUTUMN, 2"); WINTER= new T("WINTER, 3"); ENUM$VALUES = (new T[] {SPRING, SUMMER, AUTUMN, WINTER}); } public static T[] values() { T at[]; int i; T at1[]; System.arraycopy(at = ENUM$VALUES, 0, at1 = new T[i = at.length], 0, i); return at1; } public static T valueOf(String s) { ...... } }枚举类型在序列化的时候,仅仅将枚举对象的name属性输出到结果中,然后反序列化的时候则通过java.lang.Enum的valueOf方法根据名字查找枚举对象。编译器不允许任何对序列化的定制,因此禁止了writeObject, readObject等方法~
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { throw new InvalidObjectException( "can't deserialize enum"); }
public static <T extends Enum <T>> T valueOf(Class<T> enumType , String name) { T result = enumType.enumConstantDirectory().get( name); if (result != null) return result; if (name == null) throw new NullPointerException( "Name is null"); throw new IllegalArgumentException( "No enum constant " + enumType .getCanonicalName() + "." + name ); }enumType会通过反射T类的values方法,获取所有的实例!然后构建一个名字和实例对应的Map-enumConstantDirectory
应用场景:
> 线程池
> 网站计数器
> 数据库连接池
> 任务管理器
公共访问节点,生成唯一标识等,比如线程池,网站计数器,数据库连接池:
public class Test1{ private static Test1 t1= null; private int i = 0; private Test1() {} public static Test1 getInstance() { if(t1 == null) synchronized(Test1.class) { if(t1 == null) t1 = new Test1(); } return t1; } public void add(String s) throws InterruptedException { System.out.println(s); Thread.sleep(500); System.out.println(i++); } public static void main(String[] args) { Thread t1 = new Thread(new T1()); t1.start(); Thread t2 = new Thread(new T2()); t2.start(); } } class T1 implements Runnable { @Override public void run() { Test1 t1 = Test1.getInstance(); try { t1.add("T1"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } class T2 implements Runnable { @Override public void run() { Test1 t1 = Test1.getInstance(); try { t1.add("T1"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
http://blog.csdn.net/likika2012/article/details/11483167
2. RandomAccess接口,支持快速随机访问
3. Clonable接口,支持被克隆
public Object clone() { try { ArrayList<?> v = (ArrayList<?>) super.clone(); v. elementData = Arrays. copyOf(elementData, size); v. modCount = 0; return v; } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw new InternalError( e); } }所有的类都默认继承Object,所以都可以调用super.clone来进行浅拷贝(只拷贝对象引用而不是具体的值),也可以重写clone来实现深拷贝~
- x.clone() != x - x.clone().getClass() = x.getClass() 两个对象是同一个类型 - x.clone().equals(x) 内容一样Arrays.copyOf依赖于System.arraycopy,都是浅拷贝
修改
懒惰拷贝:浅拷贝 ---> 深拷贝
可以通过序列化来实现深拷贝~
三者都是标识接口,没有任何方法和属性,当一个类实现了一个标识接口之后就像是给自己打了个标签:
private void writeObject0(Object obj, boolean unshared) throws IOException { ... if (obj instanceof String) { writeString((String) obj, unshared); } else if (cl.isArray()) { writeArray(obj, desc, unshared); } else if (obj instanceof Enum) { writeEnum((Enum) obj, desc, unshared); } else if (obj instanceof Serializable) { writeOrdinaryObject(obj, desc, unshared); } else { if (extendedDebugInfo) { throw new NotSerializableException(cl.getName() + "\n" + debugInfoStack.toString()); } else { throw new NotSerializableException(cl.getName()); } } ... }二、ArrayList源码分析
1. 属性
transient Object[] elementData; private int size; private static final int DEFAULT_CAPACITY = 10; private static final Object[] EMPTY_ELEMENTDATA = {}; private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
为什么elementData是transient?
因为elementData是一个缓存数组,所以有些空间并没有元素,都序列化的话,太浪费空间了~
private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException{ // Write out element count, and any hidden stuff int expectedModCount = modCount; s.defaultWriteObject(); // Write out size as capacity for behavioural compatibility with clone() s.writeInt(size); // Write out all elements in the proper order. for (int i=0; i<size; i++) { s.writeObject(elementData[i]); } if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } }2. 构造方法
public ArrayList(int initialCapacity ) { if (initialCapacity > 0) { this. elementData = new Object[ initialCapacity]; } else if (initialCapacity == 0) { this. elementData = EMPTY_ELEMENTDATA; } else { throw new IllegalArgumentException( "Illegal Capacity: "+ initialCapacity); } }
public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
public ArrayList(Collection<? extends E> c) { elementData = c.toArray(); if ((size = elementData.length) != 0) { // c.toArray might (incorrectly) not return Object[] (see 6260652) if ( elementData.getClass() != Object[]. class) elementData = Arrays. copyOf(elementData, size, Object[].class); } else { // replace with empty array. this. elementData = EMPTY_ELEMENTDATA; } }
6260652: http://www.tuicool.com/articles/uIBB3q,http://blog.csdn.net/u014082714/article/details/51811998
Arrays有一个内部类ArrayList(不支持add,remove等改变list方法),asList会返回该ArrayList,而不是java.util.ArrayList~
3. 元素存储
public E set( int index, E element) { rangeCheck( index); E oldValue = elementData( index); elementData[index] = element; return oldValue; } private void rangeCheck( int index) { if (index >= size) throw new IndexOutOfBoundsException(outOfBoundsMsg(index )); }
public void add(int index, E element) { rangeCheckForAdd( index); ensureCapacityInternal( size + 1); // Increments modCount!! System.arraycopy(elementData , index , elementData , index + 1, size - index); elementData[index] = element; size++; } private void rangeCheckForAdd (int index ) { if (index > size || index < 0) throw new IndexOutOfBoundsException(outOfBoundsMsg(index )); }
public boolean add(E e) { ensureCapacityInternal( size + 1); // Increments modCount!! elementData[size++] = e; return true; } private void ensureCapacityInternal (int minCapacity ) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math. max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity (int minCapacity ) { modCount++; // overflow-conscious code if (minCapacity - elementData.length > 0) grow( minCapacity); }
private static final int MAX_ARRAY_SIZE = Integer. MAX_VALUE - 8; private void grow( int minCapacity) { // overflow-conscious code int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // minCapacity is usually close to size, so this is a win: elementData = Arrays.copyOf(elementData , newCapacity ); } private static int hugeCapacity (int minCapacity ) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer. MAX_VALUE : MAX_ARRAY_SIZE; }容量扩增:
- 如果是空实例{},就采用默认的容量10
- 判断minCapacity是否overflow(大于Integer.MAX_VALUE的话会溢出为负)或是否大于数组长度(minCapacity = size + n, minCapacity - elementData.length?>0)
1)newCapacity = 1.5*oldCapacity
2)如果newCapacity溢出或者小于minCapacity(要保证最小空间大小)的情况下,newCapacity重新取值minCapacity
3)如果newCapacity大于MAX_ARRAY_SIZE,就判断minCapacity是否大于MAX_ARRAY_SIZE,大于就取Integer.MAX_VALUE,否则就取MAX_ARRAY_SIZE~(继续用newCapacity很容易就溢出,但是实际上很多空间都还没装满元素,用minCapacity即满足空间要求,又不会太浪费)
public boolean addAll(Collection<?extendsE>c) { Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal (size + numNew ); // Increments modCount System.arraycopy(a , 0, elementData , size , numNew ); size += numNew; return numNew != 0; } public boolean addAll(int index , Collection<? extends E> c) { rangeCheckForAdd( index); Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal( size + numNew); // Increments modCount int numMoved = size - index; if (numMoved > 0) System. arraycopy(elementData, index, elementData, index + numNew, numMoved); System.arraycopy(a , 0, elementData , index , numNew ); size += numNew; return numNew != 0; }
4. 删除元素
public E remove(int index ) { rangeCheck( index); modCount++; E oldValue = elementData( index); int numMoved = size - index - 1; if (numMoved > 0) System. arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work return oldValue; }
public boolean remove(Object o ) { if (o == null) { for ( int index = 0; index < size; index++) if ( elementData[ index] == null) { fastRemove( index); return true; } } else { for ( int index = 0; index < size; index++) if ( o.equals( elementData[ index])) { fastRemove( index); return true; } } return false; } private void fastRemove(int index) { modCount++; int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work }ArrayList元素可以是null~
protected void removeRange( int fromIndex, int toIndex) { modCount++; int numMoved = size - toIndex; System.arraycopy(elementData , toIndex , elementData , fromIndex, numMoved); // clear to let GC do its work int newSize = size - (toIndex-fromIndex); for (int i = newSize; i < size; i++) { elementData[ i] = null; } size = newSize; }
removeRange为protected方法,所以继承该类才能被使用,一般使用list.subList(start, end).clear();来代替removeRange,而clear实质也是调用SubList里面的removeRange方法。
快速失败机制:依赖于modcount,在面对并发的修改时,迭代器很快就会完全失败,而不是冒着将来某个不确定时间发生任意不确定行为的风险
Reference:
http://blog.csdn.net/li295214001/article/details/48135939
http://blog.jobbole.com/94074/
http://blog.csdn.net/goodlixueyong/article/details/51935526
http://www.blogjava.net/kenzhh/archive/2013/03/15/357824.html
http://blog.csdn.net/likika2012/article/details/11483167
http://developer.51cto.com/art/201202/317181.htm
http://stackoverflow.com/questions/2289183/why-is-javas-abstractlists-removerange-method-protected
http://blog.csdn.net/moreevan/article/details/6698529
http://www.cnblogs.com/ITtangtang/p/3948555.html
http://yikun.github.io/2015/04/04/Java-ArrayList%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86%E5%8F%8A%E5%AE%9E%E7%8E%B0/
相关文章推荐
- 棍子节快乐~ 顺便转一篇weka源码分析解析~【转自weka中文网】
- ArrayList源码分析
- Android 数据Parcel序列化过程源码分析
- ArrayList源码分析(基于JDK1.6)
- 集合框架源码分析三(实现类篇ArrayList,LinkedList,HashMap)
- ArrayList源码分析(基于JDK1.6)
- ArrayList源码分析
- Java concurrent Framework并发容器之CopyOnWriteArrayList(1.6)源码分析
- ArrayList源码分析
- ArrayList源码分析(基于JDK1.6)
- 源码分析之ArrayList
- android播放器(music player)源码分析3(页面解析,ArrayListCursor)
- Java Collections Framework之ArrayList源码分析
- QuickSort分析,这是回复提问时碰到的,顺便对学过的算法进行复习,总结...
- ArrayList 源码分析
- ArrayList 源码分析
- java 集合ArrayList及LinkList源码分析
- JDK源码分析之java.util.ArrayList
- 集合框架之ArrayList源码分析
- ArrayList源码分析