什么是数组,数组这种结构就是把数据码成一排进行存放.
数组是一种线性的数据结构,那么如何二次封装一个数组呢,二次封装数组的意义是什么呢?
下面先大致看一下我封装的一个数组类Array。
package com.Leo;
public class Array<E> {
private E[] data;
private int size;
//构造函数,传入数组的容量capacity构造Array
public Array(int capacity) {
data = (E[]) new Object[capacity];
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 addLast(E e) {
add(size, e);
}
//在所有元素前加一个新元素
public void addFirst(E e) {
add(0, e);
}
//在INDEX处插入新元素e
public void add(int index, E e) {
if (size == data.length) {
resize(2 * data.length);
}
if (index < 0 || index > size) {
throw new IllegalArgumentException("插入失败");
}
for (int i = size - 1; i >= index; i--) {
data[i + 1] = data[i];
}
data[index] = e;
size++;
}
//获取index索引位置的元素
public E get(int index) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException("获取失败,索引错误");
}
return data[index];
}
//修改数组中索引元素
public void set(int index, E e) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException("修改失败,索引错误");
}
data[index] = e;
}
//查找数组中是否有元素e
public boolean contains(int 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;
}
//从数组中删除index位置的元素,返回删除的元素
public E remove(int index) {
if (index < 0 || index >= size) {
throw new IllegalArgumentException("修改失败,索引错误");
}
E ret = data[index];
for (int i = index + 1; i < size; i++) {
data[i - 1] = data[i];
}
size--;
data[size] = null;
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 index = find(e);
if (index != -1) {
remove(index);
}
}
@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();
}
//扩容缩容
private void resize(int newCapacity) {
E[] newData = (E[]) new Object[newCapacity];
for (int i = 0; i < size; i++) {
newData[i] = data[i];
}
data = newData;
}
}
我们在IDEA编译器中New一个数组,并且查看它封装的方法:
因为数组是一个最基础的数据结构,所以JAVA并没有封装太多的方法进去,当我们想要对数组进行增删改查时,可能我们需要这样(以删除、排序为例):
删除数组中某一个元素:
String[] str = {a,b,c,d,e,f};
for (int i=0;i<str.length;i++){
if("d".equals(str[i])){
str[i] = null;
}
}
删除某一个元素后再把后面的元素向前移动,保证数组中没有空元素:
String[] str = {a, b, c, d, e, f};
int flag = 0;
for (int i = str.length - 1; i >= 0; i--) {
if ("d".equals(str[i])) {
flag = i;
}
}
for (int j = flag; j < str.length - 1; j++) {
str[j] = str[j + 1];
}
当我们在程序中多次操作数组,向其中进行增删改查的操作,我们为什么不再次封装一些方法,来减少重复代码,让程序更加整洁,也使得对数组的操作更加方便,便于维护。
比如,在我的Array数组类中,有如下几个特点
1.使用泛型,Array[E],把数组设计成泛型的,可以使得我们不只创建一个String类型的数组,我们可以使用所有类型来创建数组,让整个项目都可以使用这个类来创造数组。
2.数组是动态扩容,动态缩容的:当我们调用数组类中的add方法时,当要加入元素的索引等于数组大小时,我们动态的创造一个新的数组,设置新的数组的大小是旧的2倍,然后把新的数组赋给旧的数组,然而旧的数据没有地方使用,就交给垃圾回收来清理掉它,此时我们就把数组的大小扩大了一倍,缩容同理,代码如下:
//对数组扩容,缩容
private void resize(int newCapacity) {
E[] newData = (E[]) new Object[newCapacity];
for (int i = 0; i < size; i++) {
newData[i] = data[i];
}
data = newData;
}
3.对数组封装的增删改查方法的算法复杂度都是O(n)级别的,甚至是O(1)级别的(比如addFirst,addLast这些触发索引的方法),所以使用起来十分高效。
总结:针对于以上三点,我认为,当我们在项目中大量选用了数组这种数据结构,并对其进行了一些增删改查的操作,不如我们去封装一个操作数组的方法的类,这样会使我们的代码更统一,优雅,高效,便于维护。