ArrayList
ArrayList是一个数组队列,相当于动态数组,与Java中的数组相比,它没有固定大小的限制,其容量可以动态增长。
在添加大量元素前,应用程序可以使用ensureCapacity方法来增加ArrayList实例的容量。这样可以减少递增式再分配的数量
/**
* 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.
*
* @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继承和实现了什么东西
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
// ......
}
继承了AbstractList抽象类,实现了List、RandomAccess、Cloneable、java.io.Serializable这些接口
List:表明ArrayList是一个列表,支持添加、删除、查找等操作,并且可以通过下标进行访问RandomAccess:是一个标志接口,表明实现这个接口的List集合是支持快速随机访问的,在ArrayList中,我们可以通过元素的下标快速获取元素对象,这就是快速随机访问Cloneable:表明ArrayList具有拷贝能力,可以进行深拷贝或者浅拷贝Serializable:表明ArrayList可以进行序列化操作,也就是可以将对象转换为字节流进行持久化存储或网络传输
在IDEA中的类图:
ArrayList和Vector的区别?
ArrayList:是List的主要实现类,底层使用Object[]进行存储适用于频繁的查找工作,线程不安全Vector:List的古老实现类,底层使用Object[]存储,线程安全
ArrayList可以添加null值吗?
可以,AarrayList中可以存储任何类型的对象,包括null值
@Test
public void testArrayList() {
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("String");
arrayList.add(null);
System.out.println(arrayList);
}
运行结果:
[String, null]
虽然可以添加null值,但是不建议,因为null值无意义,而且会让代码难以维护,比如忘记做判空处理就会导致空指针异常(NPE)
ArrayList与LinkedList的区别?
线程安全?ArrayList和LinkedList都是不同步的,也就是不保证线程安全
底层数据结构?ArrayList底层使用Object[]数组LinkedList底层使用双向链表数据结构(JDK1.6以前为循环链表,JDK1.7取消了循环。注意不是双向循环链表)
插入和删除是否受元素位置的影响?ArrayList采用数组存储,所以插入和删除的时间复杂度受元素位置影响执行
add(E e)方法,默认将元素加到元素列表的末尾,这种情况的时间复杂度就是O(1)如果是指定位置插入和删除元素(
add(index index, E element)),时间复杂度为O(n)LinkedList采用双向链表存储,所以在头尾插入和删除元素不受元素位置的影响例如:add(E e)、addFirst(E e)、removeFirst()、removeLast()时间复杂度是0(1)如果是指定位置插入和删除元素(
add(int index, E element),remove(Object o)、remove(int index))时间复杂度为O(n)
是否支持快速随机访问?(快速随机访问:通过元素的序号快速获取元素对象(get(int index)))LinkedList不支持高效的随机元素访问ArrayList因为实现了RandomAccess接口,所以支持
内存空间占用ArrayList的空间花费主要体现在:在list列表的结尾会预留一定的容量空间LinkedList的空间花费主要体现在:它的每一个元素都要消耗比ArrayList更多的空间(因为要存放直接前驱和直接后继和数据)
ArrayList核心源码
属性值
// 序列号
private static final long serialVersionUID = 8683452581122892189L;
// 默认初始容量大小
private static final int DEFAULT_CAPACITY = 10;
// 空数组(用于空实例)
private static final Object[] EMPTY_ELEMENTDATA = {};
// 用于默认大小空实例的共享空数组实例
// 把他从EMPTY_ELEMENTDATA数组中区分出来,以知道在添加第一个元素时容量要增加多少
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 保存ArrayList数据的数组
transient Object[] elementData; // non-private to simplify nested class access
// ArrayList所包含的元素个数
private int size;
// 要分配的最大数组大小
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
构造器
带初始容量参数的构造器(用户可以在创建ArrayList对象时自己指定集合的初始大小)
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); } }默认无参构造器(
DEFAULTCAPACITY_EMPTY_ELEMENTDATA为0,初始化为10就是说初始其实是空数组,只有当添加第一个元素的时候数组容量才变成10)public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } private static int calculateCapacity(Object[] elementData, int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } return minCapacity; }可以指定集合的元素列表的构造器,按照他们由集合的迭代器返回的顺序
public ArrayList(Collection<? extends E> c) { Object[] a = c.toArray(); if ((size = a.length) != 0) { if (c.getClass() == ArrayList.class) { elementData = a; } else { elementData = Arrays.copyOf(a, size, Object[].class); } } else { // replace with empty array. elementData = EMPTY_ELEMENTDATA; } }
方法
修改当前ArrayList实例的容量为列表当前的大小,可以使用该方法来时ArrayList实例的存储最小化
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size);
}
}
ArrayList的扩容机制
AarrayList的扩容机制提高了性能,如果每次之扩充一个,那么频繁的插入会导致频繁地拷贝,降低性能,而ArrayList的扩容机制避免了这个问题
// 如果有必要,增加当前AarrayList实例的容量,以确保其至少能容纳元素的数量
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;
// 如果最小容量大于已有的最大容量,就调用ensureExplicitCapacity方法判断是否扩容
if (minCapacity > minExpand) {
ensureExplicitCapacity(minCapacity);
}
}
// 根据给定的最小容量和当前数组元素来计算所需容量
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 如果当前数组元素为空数组,返回默认容量和最小容量中的较大值作为所需容量
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
// 否则直接返回最小容量
return minCapacity;
}
// 确保内部容量达到指定的最小值
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// 判断是否需要扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
// 调用grow方法进行扩容,调用grow方法就代表已经开始扩容了
grow(minCapacity);
}
// MAX_ARRAY_SIZE:要分配的最大数组大小
// ArrayList扩容的核心方法
private void grow(int minCapacity) {
// oldCapacity:旧容量
// newCapacity:新容量
int oldCapacity = elementData.length;
// 新容量 = 旧容量 + 旧容量 / 2(左移一位就相当于除以2)
// 位运算速度远远快于整除运算,新容量等于1.5倍的旧容量
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 检查新容量是否大于最小需要容量,如果小于则将最小需要容量当作数组的新容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 再次检查新容量是否超出了ArrayList所定义的最大容量
// 如果超出了就调用hugeCapacity方法来比较minCapacity来比较minCapacity和
// MAX_ARRAY_SIZE,如果minCapacity大于MAX_ARRAY_SIZE,则新容量为Integer.MAX_VALUE
// 否则新容量就是MAX_ARRAY_SIZE
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);
}
// 比较minCapacity和MAX_ARRAY_SIZE
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
基本方法
// 返回当前ArrayList中的元素数
public int size() {
return size;
}
// 判断当前ArrayList是否为空
public boolean isEmpty() {
return size == 0;
}
// 判断当前ArrayList中是否包含指定的元素
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
// 返回当前ArrayList中指定元素首次出现对应的索引,如果没找到就返回-1
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;
}
// 返回当前ArrayList中指定元素的最后一次出现的索引,找不到就返回-1
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;
}
// 返回当前ArrayList对象的浅拷贝,元素本事不被复制
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对象中所有元素的数组
// 返回的数组是安全地,因为当前ArrayList对象不保留对其的引用(即调用该方法就必须要分配一个新的数组)
// 因此调用者可以自由地修改返回的数组
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
// 以正确的顺序返回一个包含当前ArrayList对象中所有元素的数组
// 返回的数组的运行时类型是指定数组的运行时类型,如果当前ArrayList对象适合指定的数组,则返回其中
// 否则将为指定数组的运行时类型和当前ArrayList对象的大小分配一个新数组
// 如果ArrayList对象适用于指定的数组,其余空间(数组长度大于当前ArrayList对象的长度)
// 则紧跟在集合结束后的数组中的元素设置为null
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
if (a.length < size)
// 新建一个运行时类型的数组,但是是ArrayList数组的内容
return (T[]) Arrays.copyOf(elementData, size, a.getClass());
// 调用System提供的arraycopy方法,实现数组之间的复制
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
// Positional Access Operations
@SuppressWarnings("unchecked")
E elementData(int index) {
return (E) elementData[index];
}
// 返回指定索引位置的元素
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
// 用指定索引以及元素替换掉当前ArrayList对象中指定位置的元素
public E set(int index, E element) {
// 对index索引进行界限检查
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
// 返回原来在这个位置的元素
return oldValue;
}
// 将元素e添加到当前ArrayList对象的末尾
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
// 在当前ArrayList中的指定index位置中插入元素
public void add(int index, E element) {
// 对index索引进行界限检查
rangeCheckForAdd(index);
// 保证capacity足够大
ensureCapacityInternal(size + 1); // Increments modCount!!
// 将index开始后的所有元素往后移一位,将element插入到指定index位置,再让size+1
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
// 移除指定index索引处的元素,并将后续元素往左移动一位
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;
}
// 从当前ArrayList对象中删除指定元素的第一个出现,如果没找到则不会更改
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对象中所有元素
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
// 按指定集合的Iterator返回的顺序,将指定集合c中的所有元素追加到当前ArrayList对象的末尾
public boolean addAll(Collection<? extends E> 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;
}
// 从此列表中删除所有索引为fromIndex (含)和toIndex之间的元素
// 将任何后续元素移动到左侧(减少其索引)
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;
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, true);
}
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
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();
}
}
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
elementData = EMPTY_ELEMENTDATA;
// Read in size, and any hidden stuff
s.defaultReadObject();
// Read in capacity
s.readInt(); // ignored
if (size > 0) {
// be like clone(), allocate array based upon size not capacity
int capacity = calculateCapacity(elementData, size);
SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
ensureCapacityInternal(size);
Object[] a = elementData;
// Read in all elements in the proper order.
for (int i=0; i<size; i++) {
a[i] = s.readObject();
}
}
}
public ListIterator<E> listIterator(int index) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index);
}
public ListIterator<E> listIterator() {
return new ListItr(0);
}
public Iterator<E> iterator() {
return new Itr();
}
AarrayList扩容机制
以无参构造器创建的ArrayList来分析一下
默认无参构造器(DEFAULTCAPACITY_EMPTY_ELEMENTDATA为0,初始化为10就是说初始其实是空数组,只有当添加第一个元素的时候数组容量才变成10)
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
add()
// 将元素e添加到当前ArrayList对象的末尾
public boolean add(E e) {
// 在添加元素之前先调用ensureCapacityInternal方法,以确保内部容量达到指定的最小容量
// size:当前ArrayList对象拥有的元素数量
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
JDK11移除了ensureCapacityInternal()和ensureExplicitCapacity()方法
ensureCapacityInternal:// 确保内部容量达到指定的最小容量。 private void ensureCapacityInternal(int minCapacity) { ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); }
ensureCapacityInternal内部直接调用了ensureExplicitCapacity方法
calculateCapacity:// 根据给定的最小容量和当前数组元素来计算所需容量。 private static int calculateCapacity(Object[] elementData, int minCapacity) { // 如果当前数组元素为空数组(初始情况),返回默认容量和最小容量中的较大值作为所需容量 if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { return Math.max(DEFAULT_CAPACITY, minCapacity); } // 否则直接返回最小容量 return minCapacity; }
ensureExplicitCapacity://判断是否需要扩容 private void ensureExplicitCapacity(int minCapacity) { modCount++; //判断当前数组容量是否足以存储minCapacity个元素 if (minCapacity - elementData.length > 0) //调用grow方法进行扩容 grow(minCapacity); }
- 当我们
add第一个元素到ArrayList时,elementData.length为 0 (因为还是一个空的 list),因为执行了ensureCapacityInternal()方法 ,所以minCapacity此时为 10。此时,minCapacity - elementData.length > 0成立,所以会进入grow(minCapacity)方法。 - 当
add第 2 个元素时,minCapacity为 2,此时elementData.length(容量)在添加第一个元素后扩容成10了。此时,minCapacity - elementData.length > 0不成立,所以不会进入 (执行)grow(minCapacity)方法。 - 添加第 3、4···到第 10 个元素时,依然不会执行 grow 方法,数组容量都为 10。
直到添加第11个元素,minCapacity(为11)比elementData(为10)要大,就进入grow方法进行扩容
grow()
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);
}
int newCapacity = oldCapacity + (oldCapacity >> 1);ArrayList扩充容量为原来的1.5倍由此可见,如果oldCapacity的值为偶数就是1.5倍,如果是奇数就是1.5倍左右,因为奇数在进行运算的时候会丢掉小数(33+33/2=49)
位移运算符(>>):>>1右移一位相当于除以2,
右移n位相当于除以2的n次幂这里
oldCapacity明显右移一位,所以相当于oldCapacity/2,对于大数据的二进制位运算,位移运算符比普通的运算符要快很多,因为程序仅仅只是移动了一下而非计算,这样可以提高效率节省资源
- 当我们
add第1个元素到ArrayList时,oldCapacity=0,第一个if判断成立,newCapacity = minCapacity(是10),第二个if判断不会成立,不会进入hugeCapacity,此时数组容量为10,在add()中size变为1 - 当
add到第11个元素到ArrayList时,newCapacity(为15)比minCapacity(为11)大,第一个if判断不成立,newCapacity也不大于MAX_ARRAY_SIZE(数组最大size),第二个if判断也不成立,不会进入hugeCapacity()。数组容量扩容到newCapacity即15,回到add()中size变为11。
hugeCapacity()
在grow()方法中,当我们的新容量大于数组最大size的时候就会调用hugeCapacity(),用来比较minCapacity和MAX_ARRAY_SIZE,如果minCapacity大于最大容量则新容量就为Integer.MAX_VALUE(2^31 - 1),否则就为Integer.MAX_VALUE - 8
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
public static final int MAX_VALUE = 0x7fffffff;
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
ensureCapacity()
一开始就提到过这个方法,在添加大量元素前,应用程序可以使用ensureCapacity方法来增加ArrayList实例的容量。这样可以减少递增式再分配的数量
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 添加大量元素之前用 ensureCapacity 方法,以减少增量重新分配的次数
测试:
@Test
public void testEnsureCapacity1() {
ArrayList<Integer> list = new ArrayList<>();
final int N = 10000000;
long startTime = System.currentTimeMillis();
for (int i = 0; i < N; i++) {
list.add(i);
}
long endTime = System.currentTimeMillis();
System.out.println("使用ensureCapacity方法前:"+(endTime - startTime));
}
@Test
public void testEnsureCapacity2() {
ArrayList<Integer> list = new ArrayList<>();
final int N = 10000000;
list.ensureCapacity(N);
long startTime = System.currentTimeMillis();
for (int i = 0; i < N; i++) {
list.add(i);
}
long endTime = System.currentTimeMillis();
System.out.println("使用ensureCapacity方法后:"+(endTime - startTime));
}