实现自己的ArrayList

文章目录

一,java标准库的ArrayList

1.底层结构

private static final int DEFAULT_CAPACITY = 10;
transient Object[] elementData; // non-private to simplify nested class access
private int size;
  • 默认容量是10
  • 底层实现是java数组,且支持泛型

2.增删改查

添加元素

暴露方法:

实现自己的ArrayList_数组

<Integer> list = new ArrayList<>();
list.add(3);
list.add(1,2);

深入方法内部:

public boolean add(E e) {
//判断是否需要扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
//add方法默认是在数组所有元素末添加新元素
elementData[size++] = e;
//添加成功返回true
return true;
}

时间复杂度:O(1)

关于扩容过程会在后边提及

在指定位置添加指定元素

public void add(int index, E element) {
//检查下标index是否合法
rangeCheckForAdd(index);
//判断是否需要扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
//将index下标及其后边的元素向右移动一位
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
//此时index下标空出来,直接赋值即可
elementData[index] = element;
size++;
}

根据下标查询元素

list.get(1);
public E get(int index) {
//检查下标
rangeCheck(index);
//返回指定下标元素
return elementData(index);
}

E elementData(int index) {
return (E) elementData[index];
}

时间复杂度:O(1)

修改元素

public E set(int index, E element) {
//检查下标
rangeCheck(index);
//旧值
E oldValue = elementData(index);
//直接修改
elementData[index] = element;
//返回旧值
return oldValue;
}

时间复杂度:O(1)

删除元素

public E remove(int index) {
//检查下标
rangeCheck(index);

modCount++;
//要删除的下标对应的值
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
//index下标右边的元素向左移动一位,覆盖原index元素
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
//GC
elementData[--size] = null; // clear to let GC do its work

return oldValue;
}

其实还有一个不需要检查下标的快速remove

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
}

删除指定元素

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;
}

3.扩容

public boolean add(E e) {
//判断是否需要扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}

private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

private static int calculateCapacity(Object[] elementData, int minCapacity) {
//如果当前数组是空数组,则选取默认容量和传进来的size + 1两者的最大值
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
//不是空数组则返回size + 1
return minCapacity;
}

private void ensureExplicitCapacity(int minCapacity) {
modCount++;
//判断是否大于当前数组长度,大于则需要扩容
// overflow-conscious code
if (minCapacity - elementData.length > 0)
//扩容函数
grow(minCapacity);
}

private void grow(int minCapacity) {
// overflow-conscious code
//旧数组容量
int oldCapacity = elementData.length;
//新数组容量 = 旧数组容量 * 1.5
int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果新数组容量小于传进来的minCapacity,则新数组容量就是当前minCapacity
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
//当前新数组容量大于最大数组容量,则新数组容量会更新为Integer的最大数
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;
}

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object)newType == (Object)Object[].class)
? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
//扩容底层
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
//返回扩容后新数组
return copy;
}

一般来说是扩容1.5倍

4.快速失败机制

快速失败机制是在java集合中的一种错误检测机制,当遍历集合的时候,如果集合结构发生改变,比如新增或者删除了集合中的元素,就会抛出ConcurrentModificationException异常
fail-fast机制并不保证在不同步的修改下一定会抛出异常,在我们常见的java集合中就可能出现fail-fast机制,比如ArrayList,HashMap。在多线程和单线程环境下都有可能出现快速失败。

原理:

原因:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量。集合在被遍历期间如果内容发生变化,就会改变modCount的值(无论是add()、remove(),还是clear(),
只要涉及到修改集合中的元素个数时,都会改变modCount的值)。每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历;否则抛出异常,终止遍历

final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}

二,自己的ArrayList

1.底层结构

底层实现也是java的数组,也支持泛型

private static int INIT_CAPACITY = 10;
private int size;
private E[] data;

2.增删改查

添加元素

public void addFirst(E val){
add(0,val);
}

public void addLast(E val){
add(size,val);
}

public void add(int index,E val){
if(index < 0 || index > size) {
throw new IllegalArgumentException("Add failed. Require index >= 0 and index <= size.");
}
if(size == data.length) {
resize(2 * data.length);
}
for(int i = size - 1;i >= index;i--) {
data[i + 1] = data[i];
}
data[index] = val;
size++;
}

根据下标查询元素

public E get(int index){
if(index < 0 || index >= size || index >= data.length){
throw new IllegalArgumentException("index is Illegal");
}
E res = data[index];
return res;
}

修改元素

public void set(int index, E e){
if(index < 0 || index >= size) {
throw new IllegalArgumentException("Set failed. Index is illegal.");
}
data[index] = e;
}

删除元素

public E removeFirst(){
return removeByIndex(0);
}

public E removeLast(){
return removeByIndex(size - 1);
}

public E removeByVal(E val){
if(!contains(val)){
return null;
}
return removeByIndex(indexOf(val));
}

public E removeByIndex(int index){
if(index < 0 || index >= size) {
throw new IllegalArgumentException("Remove failed. Index is illegal.");
}
E delVal = data[index];
for(int i = index + 1;i < size;i++){
data[i - 1] = data[i];
}
size--;
return delVal;
}

3.扩容

我实现的扩容原理跟ArrayList差不多,也是将旧数组的元素搬到新数组后返回新数组,但是相比标准库中的ArrayList少了很多判断

private void resize(int newCapacity) {
E[] newData = (E[]) new Object[newCapacity];
for(int i = 0;i < size;i++){
newData[i] = data[i];
}
data = newData;
}

源码:

package 数据结构.array;

import java.util.ArrayList;

/**
* @Auther: Kevin
* @Date:
* @ClassName:Array
* @Description: TODO
*/
public class Array<E> {

private static int INIT_CAPACITY = 10;
private int size;
private E[] data;

public Array(int capacity){
data = (E[])new Object[capacity];
size = 0;
}

public Array(){
this(INIT_CAPACITY);
}

public boolean isEmpty(){
return size == 0;
}

public int getSize(){
return size;
}

public int getCapacity(){
return data.length;
}


public void addFirst(E val){
add(0,val);
}

public void addLast(E val){
add(size,val);
}

public void add(int index,E val){
if(index < 0 || index > size) {
throw new IllegalArgumentException("Add failed. Require index >= 0 and index <= size.");
}
if(size == data.length) {
resize(2 * data.length);
}
for(int i = size - 1;i >= index;i--) {
data[i + 1] = data[i];
}
data[index] = val;
size++;
}

private void resize(int newCapacity) {
E[] newData = (E[]) new Object[newCapacity];
for(int i = 0;i < size;i++){
newData[i] = data[i];
}
data = newData;
}

public boolean contains(E val){
for(int i = 0;i < size;i++){
if(data[i] == val){
return true;
}
}
return false;
}

public int indexOf(E val){
for(int i = 0;i < size;i++){
if(data[i] == val){
return i;
}
}
return -1;
}

public E removeFirst(){
return removeByIndex(0);
}

public E removeLast(){
return removeByIndex(size - 1);
}

public E removeByVal(E val){
if(!contains(val)){
return null;
}
return removeByIndex(indexOf(val));
}

public E removeByIndex(int index){
if(index < 0 || index >= size) {
throw new IllegalArgumentException("Remove failed. Index is illegal.");
}
E delVal = data[index];
for(int i = index + 1;i < size;i++){
data[i - 1] = data[i];
}
size--;
return delVal;
}

public E get(int index){
if(index < 0 || index >= size || index >= data.length){
throw new IllegalArgumentException("index is Illegal");
}
E res = data[index];
return res;
}

public void set(int index, E e){
if(index < 0 || index >= size) {
throw new IllegalArgumentException("Set failed. Index is illegal.");
}
data[index] = e;
}

@Override
public String toString(){
StringBuilder res = new StringBuilder();
res.append(String.format("Array: size = %d , capacity = %d\n", size, data.length));
res.append('[');
for(int i = 0 ; i < size ; i ++){
res.append(data[i]);
if(i != size - 1) {
res.append(", ");
}
}
res.append(']');
return res.toString();
}
}