我们都知道java提供的数组一旦初始化后,长度是固定的无法进行扩容。所以今天我们对数组进行封装重新编写一个更强大的数组(也可以看做简单版ArrayList)
//定义一个数组类
public class Array<E> {
//默认数组
private E [] data;
//数组元素个数
private int size;
//初始化指定容量的 构造函数
public Array(int initCapacity){
//java不支持直接new 泛型类型的数组所以需要进行类型转换
data = (E[]) new Object[initCapacity];
size = 0;
}
// 无参构造函数默认容量10
public Array(){
this(10);
}
//获取数组元素个数
public int getSize(){
return size;
}
//获取数组容量
public int getCapacity(){
return data.length;
}
//数组是否为空
public boolean isEmpty(){
return size == 0;
}
//向指定索引位置添加元素
public void add(int index,E e){
if(index < 0 || index > size){
//index < size是为了防止数组中间出现空隙,从而保证数组数据紧密连续的在一起
throw new IllegalArgumentException("array is error");
}
if(size == data.length){
//插入数据时如果当前数组已满,需要对数组进行扩容,扩容现有数组的两倍而jdk是1.5倍
resize(2*data.length);
}
//在指定索引位置插入元素时,要将该位置及其后面的元素往后移动一个位置
for(int i = size - 1; i >= index; i--){
data[i + 1] = data[i];
}
data[index] = e;
size++;
}
//向数组末尾添加元素
public void addLast(E e){
add(size,e);
}
//向数组头部添加元素
public void addFirst(E e){
add(0,e);
}
//数组扩容,新建一个容量为原数组两倍的新数组,然后将原数组元素添加到新数组,
//然后将新数组引用赋值给原数组
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 E get(int index){
if(index < 0 || index > size){
throw new IllegalArgumentException("index is illegal");
}
return data[index];
}
//修改元素
public void set(int index,E e){
if(index < 0 || index > size){
throw new IllegalArgumentException("index is illegal");
}
data[index] = e;
}
//数组是否包含某个元素
public boolean contains(E e){
for(int i = 0; i < size; i++){
if(data[i].equals(e)){
return true;
}
}
return false;
}
//查找数组元素e所在的索引,如果不存在则返回-1
public int find(E e){
for(int i = 0; i < size; i++){
if(data[i].equals(e)){
return i;
}
}
return -1;
}
//删除指定索引位置的元素,并返回删除的元素
public E remove(int index){
if(index < 0 || index > size){
throw new IllegalArgumentException("index is illegal");
}
E ret = data[index];
//删除指定位置索引同样需要移动其他元素
for(int i = index + 1; i < size; i++){
data[i-1] = data[i];
}
//如果元素是引用类型数据,为避免一直占据内存空间,需要将其赋为null
data[size] = null;
size--;
//缩容
if(size == data.length/4 && data.length /2 !=0){
resize(data.length/2);
}
return ret;
}
//从数组中删除第一个元素
public E removeFirst(){
return remove(0);
}
//从数组中删除最后一个元素
public E removeLast(){
return remove(size - 1);
}
//从数组删除某个元素
public void removeElement(E e){
int res = find(e);
if(res != -1){
remove(res);
}
}
//获取最后一个元素
public E getLast(){
return get(size-1);
}
@Override
public String toString(){
StringBuilder sb = new StringBuilder();
sb.append(String.format("Array:size = %d,capacity = %d\n",size,data.length));
sb.append("[");
for(int i = 0; i < size; i++){
sb.append(data[i]);
if(i != size - 1){
sb.append(",");
}
}
sb.append("]");
return sb.toString();
}
}
由此我们可以判断:通过索引进行的查找操作时间复杂度为O(1)
addLast()理想情况下为O(1),如果触发扩容则为O(n)。
因此我们在使用ArrayList 集合的时候如果数据量较大而且数据数量已知的时候,
初始化时指定容量大小可以获得较大的性能提升。
下期我们根据封装好的数组实现“ 栈 ” 这种数据结构
———本文源于日常学习笔记记录,如有错误欢迎批评指正