Tear ArrayList a mano

¡Acostúmbrate a escribir juntos! Este es el octavo día de mi participación en el "Nuggets Daily New Plan · April Update Challenge", haz clic para ver los detalles del evento .

Los estudiantes que hayan estudiado Java sabrán que ArrayList es una colección en Java. ArrayList es una matriz dinámica, y su capa inferior es una implementación de matriz.Leamos el código fuente de ArrayList. Primero mire su constructor sin argumentos:

	public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
复制代码

Entre ellos, DEFAULTCAPACITY_EMPTY_ELEMENTDATA es una matriz vacía de tipo Objeto.

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
复制代码

Entonces, se crea una matriz vacía de tipo Objeto a través del método de construcción sin argumentos, y luego observe el método add():

	public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
复制代码

Antes de agregar un elemento a la matriz, llama al método sureCapacityInternal(), así que echemos un vistazo a ese método:

	private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
复制代码

Este método continúa llamando al método sureExplicitCapacity() y calcula la capacidad a través del método computeCapacity():

	private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
复制代码

Encuentre el valor máximo de los parámetros pasados ​​y DEFAULT_CAPACITY, el parámetro del método es el tamaño de la matriz actual y DEFAULT_CAPACITY es la capacidad inicial de la matriz:

private static final int DEFAULT_CAPACITY = 10;
复制代码

En resumen, ArrayList primero inicializa una matriz vacía y luego solicita suficiente espacio de memoria antes de agregar elementos, y la capacidad inicial de la aplicación es 10.

Otros métodos son relativamente simples y no se analizarán uno por uno. Dado que ArrayList es una implementación de una matriz, la búsqueda es rápida, pero las adiciones y eliminaciones son lentas.

Implementar ArrayList

Los artículos en la columna de estructura de datos se han actualizado varias veces. En este artículo, implementaremos ArrayList y verificaremos nuestro progreso de aprendizaje. La operación es similar a la tabla de secuencia, por lo que si domina la tabla de secuencia, será muy fácil implementar ArrayList.Simple.

Operación básica

Básicamente implementamos sus métodos de acuerdo con la API de ArrayList:

definición de estructura

#define InitializeSize 10		//初始容量为10

typedef struct{
	int *data;		//动态数组
	int length;		//有效元素长度
	int size;		//预分配数组大小
}ArrayList,*PArrayList;
复制代码

La estructura tiene tres variables, los primeros datos representan la matriz dinámica, la segunda longitud representa el número actual de elementos en la colección y el tercer tamaño representa la capacidad máxima de la colección.

Inicializar la colección

El método de inicialización de ArrayList fue analizado anteriormente. Aquí podemos implementarlo nosotros mismos. Puede ser más simple. Inicialmente, solicitamos directamente una memoria de matriz con una capacidad de 10. El código se implementa de la siguiente manera:

//初始化集合
ArrayList CreateList(){
	ArrayList list;
	//分配动态数组内存
	list.data = (int*) malloc(sizeof(int) * InitializeSize);
	if(list.data == NULL){
		exit(-1);
	}
	//初始化长度和大小
	list.length = 0;
	list.size = InitializeSize;
	return list;
}
复制代码

Corresponde al constructor sin parámetros de ArrayList.

初始化指定容量大小的集合

ArrayList还有指定容量大小的构造方法,通过传入的参数申请数组内存空间:

//构造一个具有指定初始容量的空列表
ArrayList CreateListOfInitialCapacity(int initialCapacity){
	ArrayList list;
	//分配动态数组内存
	list.data = (int*) malloc(sizeof(int) * initialCapacity);
	if(list.data == NULL){
		exit(-1);
	}
	//初始化长度和大小
	list.length = 0;
	list.size = initialCapacity;
	return list;
}
复制代码

这个很简单,稍微修改一下即可。

添加元素

接下来我们实现ArrayList的add()方法,add()方法分为两种:

  1. 将元素添加到集合中的指定位置
  2. 将元素直接添加到集合尾部

将元素添加到集合中的指定位置

在添加元素之前,我们需要判断当前集合是否满,如果满了,则申请内存空间,我们就规定申请原来数组的两倍内存即可:

int AddOfIndexList(PArrayList pList,int pos,int val){
	int i;
	//判断pos值的合法性
	if(pos < 0 || pos > pList->length){
		return 0;
	}
	//调用扩容函数
	CapacityList(pList);
	//将插入位置后面的元素都向后移动一位
	for(i = pList->length;i > pos;--i){
		//元素后移
		pList->data[i] = pList->data[i - 1];
	}
	//插入元素值
	pList->data[pos] = val;
	//有效元素长度加1
	pList->length++;
	return 1;//添加成功,返回1
}
复制代码

先判断指定的位置是否符合当前集合,然后调用扩容函数,接着讲插入位置后面的元素都向后移动一位,最后将指定位置的元素值修改为添加的元素值,记得集合元素个数加1。

下面是扩容函数:

void CapacityList(PArrayList pList){
	int *tmp, i, *p, *q;
    //若有效元素长度等于数组大小,则为集合扩容
    if (pList->length >= pList->size) {
    	//申请原数组两倍的存储空间作为新集合
        tmp = (int *)malloc(sizeof(int) * pList->size * 2);
        //变量p暂时存放原数组
        p = pList->data;
        //变量q暂时存放新数组
        q = tmp;
        //将原数组的元素值复制到新数组
        for (i = 0; i < pList->length; i++) {
        	//复制元素
           	*q = *p;
           	//地址后移
            p++;
            q++;
        }
        //释放原数组内存
        free(pList->data);
        //集合的数组指针指向新数组
        pList->data = tmp;
        //数组大小变为原来的两倍
        pList->size = pList->size * 2;
    }
}
复制代码

来分析一下,首先我们需要判断当前集合是否满了,判断条件为length == size,也就是当集合中的元素个数等于集合的最大容量时,说明集合满了。此时就去申请原数组的两倍内存空间作为新集合。

这里需要注意的是要用两个变量去分别存储原数组和新数组,然后通过循环将原数组的元素值复制到新数组中,最后释放原数组的内存,然后让data指向新数组,记得集合容量乘2(如果不用变量存储,会直接改变两个数组的地址,从而操作失败)。

将元素直接添加到集合尾部

添加到集合尾部就很简单了,直接看代码:

void AddList(PArrayList pList,int val){
	//调用AddListAsLocate()函数即可,位置为有效元素长度
	AddOfIndexList(pList,pList->length,val);
}
复制代码

这就是为什么我要先实现指定位置的元素插入了,这样在直接添加到集合尾部的函数中直接调用原先的函数就可以了,位置为集合的尾部。

移除集合中的所有元素

移除集合中的所有元素非常简单,直接将数组中的所有元素看做无效即可,将元素个数置为0:

void ClearList(PArrayList pList){
	//将有效元素长度置为0
	pList->length = 0;
}
复制代码

返回集合中首次出现的指定元素的索引

int IndexOfList(PArrayList pList,int val){
	int i;
	//遍历集合中的元素值
	for(i = 0;i < pList->length;++i){
		//查找元素值
		if(pList->data[i] == val)
			return i;
	}
	return 0;
}
复制代码

通过遍历集合中的所有元素来匹配指定的元素值,若匹配成功,则返回索引,若无匹配成功,则返回0。

查找集合中是否包含指定的元素

int ContainsList(PArrayList pList,int val){
	//调用IndexOfList()函数
	if(IndexOfList(pList,val))
		return 1;
	else
		return 0;
}
复制代码

该功能我们只需调用IndexOfList()函数即可,若返回值为0,则说明集合中没有指定的元素。

返回集合中指定位置上的元素

int GetList(PArrayList pList,int pos){
	//判断pos值的合法性
	if(pos < 0 || pos > pList->length - 1){
		return 0;
	}
	//返回元素值
	return pList->data[pos];
}
复制代码

某些操作非常简单,我就不分析了。

判断集合是否为空

int ListEmpty(PArrayList pList){
	//判断集合的有效元素长度是否为0
	if(pList->length == 0)
		return 1;
	else
		return 0;
}
复制代码

返回集合中最后一次出现的指定元素的索引

返回集合中指定元素最后一次出现的位置,我们可以从尾部到头部遍历集合,然后一一匹配元素值:

int LastIndexOfList(PArrayList pList,int val){
	int i;
	//从尾端开始遍历集合中的元素值
	for(i = pList->length - 1;i >= 0;--i){
		//查找元素值
		if(pList->data[i] == val)
			return i;
	}
	return 0;
}
复制代码

移除集合中指定位置上的元素

int RemoveOfIndexList(PArrayList pList,int pos){
	int i,val;
	//判断pos值的合法性
	if(pos < 0 || pos > pList->length - 1){
		return 0;
	}
	//将删除位置后面的元素都向前移动一位
	val = pList->data[pos];		//保存删除位置的元素值
	for(i = pos;i < pList->length;++i){	
		pList->data[i] = pList->data[i + 1];
	}
	//有效元素长度减1
	pList->length--;
	return val;					//返回删除的元素值
}
复制代码

Para eliminar un elemento, debe mover los elementos detrás de la posición especificada hacia adelante un lugar. Antes de eso, guarde el valor del elemento en la posición especificada, de lo contrario, será sobrescrito por los siguientes elementos, y finalmente recuerde reducir el número de elementos por 1.

elimina la primera aparición del elemento especificado en la colección (si existe)

int RemoveList(PArrayList pList,int val){
	int i;
	//遍历集合中的元素
	for (i = 0; i < pList->length; ++i){
		if(pList->data[i] == val){
			//调用RemoveOfIndexList()函数
			RemoveOfIndexList(pList,i);
			return 1;
		}
	}
	return 0;
}
复制代码

Reemplaza el elemento en la posición especificada en la colección con el elemento especificado

int SetList(PArrayList pList,int pos,int val){
	//判断pos值的合法性
	if(pos < 0 || pos > pList->length - 1){
		return 0;
	}
	//取出列表原位置上的元素
	int oldVal = pList->data[pos];
	//替换列表原位置上的元素
	pList->data[pos] = val;
	return oldVal;			//返回原位置上的元素
}
复制代码

Devuelve el número de elementos de la colección.

int ListSize(PArrayList pList){
	//返回集合大小
	return pList->length;
}
复制代码

Elimina todos los elementos de la colección con índices entre fromIndex (inclusive) y toIndex (exclusivo)

void RemoveRangeList(PArrayList pList,int fromIndex,int toIndex){
	int i,size;
	//判断fromIndex和toIndex值的合法性
	if(fromIndex < 0 || toIndex > pList->length){
		return;
	}
	//size为需要移除的元素个数
	size = toIndex - fromIndex; 
	for(i = 0;i < size;++i){
		//调用RemoveOfIndexList()函数,从fromIndex位置开始,删除size个元素
		RemoveOfIndexList(pList,fromIndex);
	}
}
复制代码

Analicemos la función, supongamos que tengo una secuencia {1,2,3,4,5}de este tipo y quiero eliminar los valores de los elementos con índices de 0 a 3. ¿Cómo lograrlo? Primero, eliminamos el valor del elemento 1 con el índice 0. Lógicamente, necesitamos eliminar el valor del elemento 2 con el índice 1, pero debe tenerse en cuenta que cuando elimina el valor del elemento 1 con el índice 0, la secuencia se convierte en {2,3,4,5}el índice del elemento. 2 sigue siendo 0 cuando se elimina. A través del análisis, solo necesitamos saber la cantidad de elementos que se eliminarán y luego comenzar desde fromIndex y eliminar la cantidad especificada de elementos.

Cambiar el tamaño de la colección al tamaño actual de la colección

void TrimToSizeList(PArrayList pList){
	int *tmp, i, *p, *q;
	//申请集合大小的存储空间
	tmp = (int*)malloc(sizeof(int) * pList->length);
	//变量p暂时存放原数组
	p = pList->data;
	//变量q暂时存放新数组
	q = tmp;
	//将原数组的元素复制到新数组
 	for (i = 0; i < pList->length; i++) {
        //复制元素
        *q = *p;
        //地址后移
        p++;
        q++;
    }
    //释放原数组内存
    free(pList->data);
    //集合的数组指针指向新数组
    pList->data = tmp;
    //数组大小变为当前列表的大小
    pList->size = pList->length;
}
复制代码

Esto es similar al funcionamiento de la función de expansión, por lo que no se analizará en detalle.

Supongo que te gusta

Origin juejin.im/post/7084973016753963016
Recomendado
Clasificación