一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第7天,点击查看活动详情。
1.数组的方法
public class Array<E> {
private E[] data;//数组
private int size;//数组长度
public Array(int capacity) {//构造函数,传入数组的容量capacity构造Array
data = (E[])new Object[capacity];
size = 0;
}
public Array() {//无参数的构造函数,默认数组的容量capacity=10
this(10);
}
}
1. 创建增删改查方法
- 自己创建数组无非就是数组的 增删改查 方法的创建,即 add,remove,set,get 方法。
- add方法:在指定索引位置插入数字,就相当于把指定索引处后的值往后移一位,即把值赋给下一个索引,一层for循环就可以,同时不要忘记size++,然后再在指定索引处赋值。
- remove方法:在指定索引位置删除数字,就相当于把指定索引处后的值往前移一位,即把值赋给上一个索引,一层for循环就可以,同时不要忘记size–。这里有所不同,即int[size]的值需不需要删除,其实这里删不删除并不影响数组的逻辑和功能实现,但本着严谨的态度,将int[size]赋值为null,节省一点内存空间。
public E remove(int index) {
//从数组中删除index位置的元素,返回删除的元素 时间复杂度:O(n/2)=>O(n)
if(index < 0 || index >= size) {
throw new IllegalArgumentException("Remove failed. Index is illegal.");
}
E ret = data[index];
for(int i = index + 1; i < size; i++) {
data[i - 1] = data[i];
}
size--;
data[size] = null;//loitering objects != memory leak
if(size == data.length / 4 && data.length / 2 != 0) {
resize(data.length / 2);
}
return ret;
}
public E removeFirst() {//时间复杂度:O(n)
return remove(0);
}
public E removeLast() {//时间复杂度:O(1)
return remove(size - 1);
}
public void removeElement(E e) {//从数组中删除元素e
int index = find(e);
if(index != -1) {
remove(index);
}
}
2. 获取数组元素
public E get(int index) {
//获取index索引位置的元素 时间复杂度:O(1)
if(index < 0 || index >= size) {
throw new IllegalArgumentException("Get failed. Index is illegal.");
}
return data[index];
}
void set(int index, E e) {
//修改index索引位置的元素 时间复杂度:O(1)
if(index < 0 || index >= size) {
throw new IllegalArgumentException("Set failed. Index is illegal.");
}
data[index] = e;
}
public boolean contains(E e) {
//查找数组中是否有元素e 时间复杂度:O(n)
for(int i = 0; i < size; i++) {
if(data[i] == e) {
return true;
}
}
return false;
}
public int find(E e) {
//查找数组中元素e所在的索引,如果不存在元素e,返回-1 时间复杂度:O(n)
for(int i = 0; i < size; i++) {
if(data[i].equals(e)) {
return i;
}
}
return -1;
}
2.泛型和动态数组的实现
1.泛型
在实际运用中,我们不能只是简单的用整数型数组来表示,这里就要引出泛型的概念。
- 泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?
- 顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
- 泛型的本质是为了
参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。 - 因为不能直接定义泛型,所以可以通过定义Object的方式来强转为泛型
- 定义了泛型之后就可以接收的多种类型,因为泛型把具体的参数类型泛化,模糊化,使得传参可以有更多种的选择。
2.动态数组
动态数组的实现即实现了数组的扩容。扩容的过程是,是否超出了默认的数组长度,超出则执行扩容方法
- 扩容是新建一个数组,新建数组的长度为原数组的2倍(ArrayList为1.5倍,通过位运算来计算),接着把原数组的值通过for循环来赋值到新数组,在接着把原数组的引用指向新数组。
- 在增加和删除的时候进行扩容和缩容(新建一个为原数组长度1/2的新数组)
- 在数组缩容的问题上,为了防止复杂度的震荡,数组在长度边缘疯狂的试探,频繁的进行扩容和缩容。(即数组长度为10,增加一个元素扩大为20,之后紧接着又删除一个元素,又缩容为10)。
- 所以缩容的时候判断数组实际长度为1/4时才进行缩容,就可以减少疯狂的试探,从而防止方法的时间复杂度一直为O(n)。