标签:sequence rop uri city ons 成功 object null 提高
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
RandomAccess
/**
* List实现使用的标记接口,标识它们支持快速(通常为恒定时间)随机访问。该接口的主要目的是允许通过算法更改其
* 以便在应用于随机访问列表或顺序访问列表是提供良好的性能
**/
public interface RandomAccess {
}
以下内容参考自 JavaGuide-randomaccess接口
RandowAccess
接口中什么都没有定义。所以,在我看来RandomAccess
接口不过是一个标识。标识什么?标识实现这个接口的类具有随机访问功能。binarySearch()
方法中,它要判断传入的list是否RandomAccess
的实例,如果是,调用indexedBinarySearch()
方法,如果不是,那么调用iteratorBinarySearch()
方法。 public static <T>
int binarySearch(List<? extends Comparable<? super T>> list, T key) {
if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
return Collections.indexedBinarySearch(list, key);
else
return Collections.iteratorBinarySearch(list, key);
}
ArrayList
实现了RandomAccess
接口,而LinkedList
没有实现。ArrayList
底层是数组,而LinkedList
底层是链表。数组天然支持随机访问,时间复杂度O(1)
,所以称为快速随机访问。链表需要遍历到特定位置才能访问特定位置的元素,时间复杂度为O(n)
,所以不支持快速随机访问。RandomAccess
接口只是标识,并不是说ArrayList
实现了RandomAccess
接口才具有快速随机访问功能,而是因为它本身支持快速随机访问才实现了该接口。Cloneable
可以参考我的另一篇文章 Cloneable接口、深复制和浅复制
Serializable
序列化接口
可以参考我的另一篇文章 Java中的序列化和反序列
还有一篇美团技术团队的文章,更加详细介绍了序列化和反序列化,侧重服务框架层面。序列化和反序列化
/**
* Default initial capacity.
*
* 默认初始化容量
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* Shared empty array instance used for empty instances.
*
* 用以表示空实例的共享空数组
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*
* 用以表示空实例的共享空数组。定义它的原因仅仅为了知道当添加第一个元素时需要扩容多少
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added.
*
* 实际存储元素的对象数组
* 当第一个元素被添加时任何一个空的(DEFAULTCAPACITY_EMPTY_ELEMENTDATA=0)ArrayList
* 将被扩容为10(DEFAULT_CAPACITY)
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
*
* ArrayList的大小
*/
private int size;
size
和elementData.length
size
和element.legth
是两个不同的概念
size
是ArrayList
的元素数量elementData.length
是ArrayList
内置数组的长度trimToSize()
方法额介绍中transient
关键字elementData
被transient
关键字修饰,要理解这个行为,首先要理解transient
关键字以下内容参考自 Java修饰符 transient,volatile等
Serilizable
接口。我们可以不必关心具体序列化的过程,只要这个类实现了Serializable
接口,这个类就可以序列化。
Serializable
接口transient
关键字。transient
使用小结ArrayList
中的transient
以下内容参考自 图解集合1:ArrayList
private transient Object[] elementData;
为什么elementData
是使用transient
修饰的呢?
ArrayList
实现了Serializable
,这意味着ArrayList
是可以被序列化的,用transient
修饰elementData
意味着不希望elementData
数组被序列化。这是为什么?size
和element.length
并不是一回事,以空参构造形式(List<Integer> list=new ArrayList()
)来说,当list.add(1)
执行之后,elementData.length=10
而size=1
elementData
呢?显然没有这个必要,因此ArrayList
重写了writeObject()
方法: 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();
}
}
defaultWriteObject()
方法序列化ArrayList
中的非transient
元素,elementData
不去序列化它,然后遍历elementData
,只序列化size=element.length
部分的元素
ArrayList()
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
ArrayList(int initialCapacity)
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);
}
}
ArrayList(Collection<? extends E> c)
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;
}
}
add(E e)
/**
* Appends the specified element to the end of this list.
* 在list尾部添加指定元素
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!! 可能溢出的代码
elementData[size++] = e;
return true;
}
ensureCapacityInternal(int minCapacity)
:
size+1
作为minCapacity
(注意这个minCapacity
,它代表本次扩容所需最小容量,是个很重要的参数)elementData[size++] = e
add
元素指向它在数组中对应位置,同时将size
累加ensureCapacityInternal(int minCapacity)
/**
* 确保容量的内部方法,命名上为了区别于对外提供的确保容量方法
*
* @param minCapacity
*/
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
minCapacity
calculateCapacity()
/**
* 计算容量
*
* @param elementData
* @param minCapacity
* @return
*/
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 若list为空
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
minCapacity
ensureExplicitCapacity(int minCapacity)
/**
* 确保准确的容量
*
* @param minCapacity
*/
private void ensureExplicitCapacity(int minCapacity) {
// AbstractList中维护的
modCount++;
// overflow-conscious code 可能溢出的代码
if (minCapacity - elementData.length > 0)
// 所需最小容量大于当前数组长度,开始扩容
grow(minCapacity);
}
modCount
AbstractList
中 /**
* The number of times this list has been <i>structurally modified</i>.
* Structural modifications are those that change the size of the
* list, or otherwise perturb it in such a fashion that iterations in
* progress may yield incorrect results.
* 该列表被结构修改的次数。结构修改是指更改列表大小或以其它方式干扰列表的方式,即正在进行的
* 迭代可能会产生错误的结果。
*
* <p>This field is used by the iterator and list iterator implementation
* returned by the {@code iterator} and {@code listIterator} methods.
* If the value of this field changes unexpectedly, the iterator (or list
* iterator) will throw a {@code ConcurrentModificationException} in
* response to the {@code next}, {@code remove}, {@code previous},
* {@code set} or {@code add} operations. This provides
* <i>fail-fast</i> behavior, rather than non-deterministic behavior in
* the face of concurrent modification during iteration.
*
* <p><b>Use of this field by subclasses is optional.</b> If a subclass
* wishes to provide fail-fast iterators (and list iterators), then it
* merely has to increment this field in its {@code add(int, E)} and
* {@code remove(int)} methods (and any other methods that it overrides
* that result in structural modifications to the list). A single call to
* {@code add(int, E)} or {@code remove(int)} must add no more than
* one to this field, or the iterators (and list iterators) will throw
* bogus {@code ConcurrentModificationExceptions}. If an implementation
* does not wish to provide fail-fast iterators, this field may be
* ignored.
*/
protected transient int modCount = 0;
grow(int minCapacity)
private void grow(int minCapacity) {
// overflow-conscious code 可能溢出的代码
int oldCapacity = elementData.length;
// 扩容核心操作,也就是常说的每次1.5倍扩容
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);
}
oldCapacity + (oldCapacity >> 1)
hugeCapacity(int minCapacity)
private static int hugeCapacity(int minCapacity) {
// 整型越界,OOM
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
// MAX_ARRAY_SIZE= 2147483639
// 若最小所需容量>有效数组上限,直接返回Integer.MAX_VALUE。有的jvm可以成功,有的就会失 // 败,但下次扩容一定OOM
// 若最小所需容量<有效数组上限,返回有效数组上限值。也就是说将1.5扩容后的新容量值收缩到
// 有限数组上限值大小。()
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
OOM,整型越界
如果对这里不太清楚的可以参考以下链接 谈谈Java中整数类型的存储方式
关于这里的hugeCapacity(int minCapacity)
以下内容参考自 stackoverflow回答
Integer.MAX_VALUE
。 因此,即使您有足够的内存来分配Integer.MAX_VALUE
元素数组,您在大多数JVM上也会出现OutOfMemoryError。 MAX_ARRAY_SIZE
假定在大多数现有JVM上都是有效的数组大小。该函数的核心作用只有一个,限制上限容量
Integer.Max
,为避免OOM,将容量设置Integer.Max
,有的虚拟机可以成功,有的也会产生OOM。trimToSize()
/**
* Trims the capacity of this <tt>ArrayList</tt> instance to be the
* list‘s current size. An application can use this operation to minimize
* the storage of an <tt>ArrayList</tt> instance.
*
* 将此ArrayList实例的容量调整为列表的当前大小。应用程序可以使用此操作来最小化
* ArrayList实例的存储。
*/
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
这里其实有两个问题要明确:即size
和elementData.length
两个概念引出的问题
它们有什么区别?
elementData.length
是ArrayList
内置数组的长度
add(E e)
时,elementData[size++] = e;
elementData
:在扩容方法grow(int minCapacity)
,elementData = Arrays.copyOf(elementData, newCapacity);
它们为什么会不相等?
elementData.length=10
ArrayList
的扩容机制:每次扩容都是在minCapacity
的基础上给较大容量,这样可以减少扩容次数。而trimToSize()
方法相当于对扩容后遗症的一种补偿外部什么时候需要使用这个函数?
ensureCapacity(int minCapacity)
/**
* Increases the capacity of this <tt>ArrayList</tt> instance, if
* necessary, to ensure that it can hold at least the number of elements
* specified by the minimum capacity argument.
*
* 如有必要,增加此ArrayList实例的容量,以确保它至少可以容纳最小容量参数
* 指定的元素数
*
* @param minCapacity the desired minimum capacity
*/
public void ensureCapacity(int minCapacity) {
int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
// any size if not default element table
? 0
// larger than default for default empty table. It‘s already
// supposed to be at default size.
: DEFAULT_CAPACITY;
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
ArrayList
的扩容机制提高了性能,如果每次只扩充一个,那么频繁的插入会导致频繁的拷贝,降低性能,而ArrayList
的扩容机制避免了这种情况。size()
/**
* Returns the number of elements in this list.
*
* 返回list中动态数组的元素数
*
* @return the number of elements in this list
*/
public int size() {
return size;
}
isEmpty()
/**
* Returns <tt>true</tt> if this list contains no elements.
*
* 判断list是否为空
*
* @return <tt>true</tt> if this list contains no elements
*/
public boolean isEmpty() {
return size == 0;
}
contains(Object o)
/**
* Returns <tt>true</tt> if this list contains the specified element.
* More formally, returns <tt>true</tt> if and only if this list contains
* at least one element <tt>e</tt> such that
* <tt>(o==null ? e==null : o.equals(e))</tt>.
*
* 判断集合是否包含元素o
*
* @param o element whose presence in this list is to be tested
* @return <tt>true</tt> if this list contains the specified element
*/
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
indexOf()
中indexOf(Object o)
/**
* Returns the index of the first occurrence of the specified element
* in this list, or -1 if this list does not contain the element.
* More formally, returns the lowest index <tt>i</tt> such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>,
* or -1 if there is no such index.
*/
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++)
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
返回从第0个元素开始的第一个指定元素,传入null也一样
@Test
public void testIndexOf() {
List<String> list = new ArrayList<>();
list.add("卡卡西");
list.add(null);
System.out.println("null index is:" + list.indexOf(null));
}
null index is:1
lastIndexOf(Object o)
/**
* Returns the index of the last occurrence of the specified element
* in this list, or -1 if this list does not contain the element.
* More formally, returns the highest index <tt>i</tt> such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>,
* or -1 if there is no such index.
*/
public int lastIndexOf(Object o) {
if (o == null) {
for (int i = size-1; i >= 0; i--)
if (elementData[i]==null)
return i;
} else {
for (int i = size-1; i >= 0; i--)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
indexOf(Object o)
方法的倒序clone()
/**
* Returns a shallow copy of this <tt>ArrayList</tt> instance. (The
* elements themselves are not copied.)
*
* 返回此ArrayList的浅表副本(元素本身不会被复制)(浅复制)
*
* @return a clone of this <tt>ArrayList</tt> instance
*/
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);
}
}
ArrayList<?> v = (ArrayList<?>) super.clone();
toArray()
/**
* Returns an array containing all of the elements in this list
* in proper sequence (from first to last element).
*
* 以正确顺组返回一个包含此列表中所有元素的数组(从第一个元素到最后一个元素)
*
* <p>The returned array will be "safe" in that no references to it are
* maintained by this list. (In other words, this method must allocate
* a new array). The caller is thus free to modify the returned array.
*
* 返回的数组将是“安全的”,因为没有对其的引用由该列表维护。(换句话说,此方法必须分配一个新数组。)
* 因此,调用者可以自由修改返回的数组。
*
* <p>This method acts as bridge between array-based and collection-based
* APIs.
*
* 此方法充当基于数组的API和基于集合的API之间的桥梁
*
* @return an array containing all of the elements in this list in
* proper sequence
*/
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
toArray(T[] a)
/**
* Returns an array containing all of the elements in this list in proper
* sequence (from first to last element); the runtime type of the returned
* array is that of the specified array. If the list fits in the
* specified array, it is returned therein. Otherwise, a new array is
* allocated with the runtime type of the specified array and the size of
* this list.
*
* 返回一个数组,该数组按正确的顺序包含此列表中的所有元素(从第一个元素到最后一个元素)
* 返回数组的运行时类型是按照指定数组的运行时类型。如果列表合适指定的数组,则将其返回。
* 否则,将使用指定数组的运行时类型和此列表的大小分配一个新数组。
*
* <p>If the list fits in the specified array with room to spare
* (i.e., the array has more elements than the list), the element in
* the array immediately following the end of the collection is set to
* <tt>null</tt>. (This is useful in determining the length of the
* list <i>only</i> if the caller knows that the list does not contain
* any null elements.)
*
* @param a the array into which the elements of the list are to
* be stored, if it is big enough; otherwise, a new array of the
* same runtime type is allocated for this purpose.
* @return an array containing the elements of the list
* @throws ArrayStoreException if the runtime type of the specified array
* is not a supertype of the runtime type of every element in
* this list
* @throws NullPointerException if the specified array is null
*/
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a‘s runtime type, but my contents:
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
ArrayList
提供了一个将List转为数组的方法。toArray()
有两个重载的方法:
list.toArray();
list.toArray(T[] a);
第一个重载方法,是将list直接转换成Object[]
数组;第二种重载方法是将list转换为你所需要的类型的数组。
String[] array =new String[list.size()];
list.toArray(array);
详情可参考该链接 集合转数组的
toArray()
和toArray(T[] a)
方法
标签:sequence rop uri city ons 成功 object null 提高
原文地址:https://www.cnblogs.com/riders/p/12938033.html