数组基础
数组作为数据结构最大的优点在于可以快速查询,arr[2]。但由于数组的大小在开始创建时就被定义,因此如果存储的内容超出了数组容量,就无法在原先的数组中保存数据。这里就需要使用一个可以动态改变数组容量的动态数组来存放数组。而在建立动态数组类前,我们首先要创建一个数组类。
这里我们假设要创建一个int类型的数组类。在开头先定义好我们这个类中有一个数组data[],以及数组中实际存放的元素数量size。初始化数组类时需要输入数组的容量capacity的值,如果没有输入则默认为10。
public class Array {
private int[] data;
private int size;
public Array(int capacity) {
this.data = new int[capacity];
size = 0;
}
public Array() {
this(10);
}
}
然后我们需要给该数组类添加增删改查四个基础功能。
首先是加的功能,参数index是要添加的位置,即数组的索引,参数element是要添加进去的元素。类似于插入的过程使得整个数组在index往后的索引值都需要往后移一位。
//添加指定索引index位置的元素data
public void add(int index, int element) {
if (size == data.length) {
throw new IllegalArgumentException("Add failed, Array is full");
}
if (index > size || index < 0) {
throw new IllegalArgumentException("Add failed, index is not under[0 - size]");
}
for (int i = size; i > index; i--) {
data[i] = data[i - 1];
}
data[index] = element;
size++;
}
我们可以通过上述的方法封装好在数组首部和尾部添加元素的方法。
public void addFirst(int element) {
add(0, element);
}
public void addLast(int element) {
add(size, element);
}
删除的方法与增加的方法类似,只不过是后续的元素要往前移一位。
//删除index位置的元素并返回该元素的值
public int delete(int index) {
if (index >= size || index < 0) {
throw new IllegalArgumentException("delete failed, index is illegal");
}
int ret = data[index];
for (int i = index; i < size; i++) {
data[i] = data[i + 1];
}
size--;
return ret;
}
public int deleteFirst() {
return delete(0);
}
public int deleteLast() {
return delete(size - 1);
}
查找和改变的方式比较简单。
//寻找数组是否包含元素e,如果包含返回索引值,否则返回-1
public int findx(int e) {
for (int i = 0; i < size; i++) {
if (data[i] == e) {
return i;
}
}
return -1;
}
//将索引上的值改变为e
public void set(int index, int e) {
if (index >= size || index < 0) {
throw new IllegalArgumentException("set failed, index is illegal");
}
data[index] = e;
}
该数组类应该要有个输出,重写Object的toString方法
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append("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();
}
泛型
由于数组不可能永远都是int类型,因此写一个泛型的数组类是必要的,我们可以通过它来创建各个类型的数组。
但是需要注意的是,使用泛型创造的数据结构不可以放置基本数据类型(int, char, boolean, double…),只能放置它们的包装类(Int, Char, Boolean, Double…)。
下面的代码是使用泛型创造的数组类:
需要注意,泛型定义数组时不可以直接new一个泛型数组,而是要new Object[]后再整形成泛型
public class Array<E> {
private E[] data;
private int size;
public Array(int capacity) {
this.data = (E[]) new Object[capacity];
size = 0;
}
public Array() {
this(10);
}
//添加指定索引index位置的元素data
public void add(int index, E element) {
if (size == data.length) {
throw new IllegalArgumentException("Add failed, Array is full");
}
if (index > size || index < 0) {
throw new IllegalArgumentException("Add failed, index is not under[0 - size]");
}
for (int i = size; i > index; i--) {
data[i] = data[i - 1];
}
data[index] = element;
size++;
}
public void addFirst(E element) {
add(0, element);
}
public void addLast(E element) {
add(size, element);
}
//删除index位置的元素并返回该元素的值
public E delete(int index) {
if (index >= size || index < 0) {
throw new IllegalArgumentException("delete failed, index is illegal");
}
E ret = data[index];
for (int i = index; i < size; i++) {
data[i] = data[i + 1];
}
size--;
return ret;
}
public E deleteFirst() {
return delete(0);
}
public E deleteLast() {
return delete(size - 1);
}
//寻找数组是否包含元素e,如果包含返回索引值,否则返回-1
public int findx(E e) {
for (int i = 0; i < size; i++) {
if (data[i] == e) {
return i;
}
}
return -1;
}
//将索引上的值改变为e
public void set(int index, E e) {
if (index >= size || index < 0) {
throw new IllegalArgumentException("set failed, index is illegal");
}
data[index] = e;
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
res.append("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();
}
}
动态数组
动态数组要实现的功能是在数组突破容量时扩大数组的容量,当数组实际存储的元素数远小于容量时缩小数组的容量。方法可以是:当数组到极限时,创建一个两倍于原数组容量的新数组,将原数组的内容复制到新数组中,并返回新数组;当数组的size小于等于1/4容量时,创建一个1/2容量的新数组,赋值进去并返回新数组。具体的代码操作只需要改变add和delete两个函数方法。
public void add(int index, E element) {
if (size == data.length) {
renew(data.length * 2);
}
if (index > size || index < 0) {
throw new IllegalArgumentException("Add failed, index is not under[0 - size]");
}
for (int i = size; i > index; i--) {
data[i] = data[i - 1];
}
data[index] = element;
size++;
}
public E delete(int index) {
if (index >= size || index < 0) {
throw new IllegalArgumentException("delete failed, index is illegal");
}
E ret = data[index];
for (int i = index; i < size; i++) {
data[i] = data[i + 1];
}
size--;
if (size == data.length / 4 && data.length / 2 != 0) {
renew(data.length/2);
}
return ret;
}
private void renew(int capacity) {
E[] dataNew = (E[]) new Object[capacity];
for (int i = 0; i < size; i++) {
dataNew[i] = data[i];
}
data = dataNew;
}
代码测试
增加元素观察是否能扩展数组容量,减少很多元素观察是否能缩小容量
public class Main {
public static void main(String[] args) {
Array<Integer> arr = new Array<>();
for(int i = 0 ; i < 10 ; i ++)
arr.addLast(i);
System.out.println(arr);
arr.add(1, 100);
System.out.println(arr);
arr.addFirst(-1);
System.out.println(arr);
arr.delete(2);
System.out.println(arr);
arr.delete(4);
System.out.println(arr);
arr.deleteFirst();
System.out.println(arr);
arr.deleteFirst();arr.deleteFirst();arr.deleteFirst();arr.deleteFirst();arr.deleteFirst();
System.out.println(arr);
}
}