在编程中,数组是一种基本的数据结构,用于存储一系列相同类型的元素。然而,传统的数组在创建时需要指定大小,这在处理动态数据时可能会带来不便。为了解决这个问题,动态数组(Dynamic Array)应运而生。动态数组能够在运行时根据需要调整其大小,从而提供更大的灵活性。虽然Java已经有ArrayList了,但是为了打好基础还是有必要自己手动过一遍这些底层原理机制的。
动态数组代码实现
以下是一个简单的动态数组实现,用Java语言编写:
package school;
import java.util.Arrays;
import java.util.Iterator;
import java.util.function.Consumer;
import java.util.stream.IntStream;
/**
* 文件名: null.java
* 作者: 20526
* 创建时间: 2024/9/5 15:44
* 描述:动态数组实现
*/
public class DynamicArray implements Iterable<Integer> {
private int size = 0;
private int capacity = 10;
private int[] arr = {};
public void addlast(int num) {
// arr[size] = num;
// size++;
add(size,num);
}
public void add(int index, int num) {
checkCapacity();
if (index >= 0 && index < size) {
System.arraycopy(arr, index, arr, index + 1, size - index);
}
size++;
arr[index] = num;
}
private void checkCapacity() {
//容量检查
if (size == 0){
arr = new int [capacity];
}
else if (size == capacity) {
//进行1.5倍扩容
capacity += capacity>>1;
int[] newArr = new int[capacity];
System.arraycopy(arr,0,newArr,0,size);
arr = newArr;
}
}
public int get(int index) {
return arr[index];
}
//排序方法
public void sort(){
Arrays.sort(arr,0,size);
}
//遍历方法
//foreach遍历
public void foreach(Consumer<Integer> consumer) {
for (int i = 0; i < size; i++) {
// System.out.println(arr[i]);
consumer.accept(arr[i]);
}
}
//迭代器遍历
@Override
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
int i = 0;
@Override
public boolean hasNext() {
return i<size;
}
@Override
public Integer next() {
return arr[i++];
}
};
}
//Stream 遍历
public IntStream stream(){
return IntStream.of(Arrays.copyOfRange(arr,0,size));
}
//删除方法
public void remove(int index){
if (index < size-1 && index >= 0) {
System.arraycopy(arr, index + 1, arr, index, size - index - 1);
}
size--;
}
}
测试代码
package school;
import org.junit.Test;
import java.util.Arrays;
import java.util.function.Consumer;
/**
* 文件名: null.java
* 作者: 20526
* 创建时间: 2024/9/5 16:06
* 描述:
*/
public class DynamicArrayTest {
@Test
public void test1() {
DynamicArray dynamicArray = new DynamicArray();
dynamicArray.addlast(20);
dynamicArray.addlast(10);
dynamicArray.addlast(30);
dynamicArray.addlast(5);
dynamicArray.foreach(num->{
System.out.println(num);
});
dynamicArray.sort();
}
@Test
public void test2() {
DynamicArray dynamicArray = new DynamicArray();
dynamicArray.addlast(1);
dynamicArray.addlast(2);
dynamicArray.addlast(3);
dynamicArray.addlast(5);
for(int elements: dynamicArray){
System.out.println(elements);
}
}
@Test
public void test3() {
DynamicArray dynamicArray = new DynamicArray();
dynamicArray.addlast(1);
dynamicArray.addlast(2);
dynamicArray.addlast(3);
dynamicArray.addlast(5);
dynamicArray.stream().forEach(element -> System.out.println(element));
}
@Test
public void test4() {
DynamicArray dynamicArray = new DynamicArray();
dynamicArray.addlast(1);
dynamicArray.addlast(2);
dynamicArray.addlast(3);
dynamicArray.addlast(5);
dynamicArray.remove(2);
dynamicArray.foreach(num->{
System.out.println(num);
});
}
}
通过上述测试代码,我们可以验证动态数组的插入、删除、遍历和排序功能是否正常工作。
关键点解析
下面所有代码都出自上面的代码片段
容量检查与扩容:
checkCapacity()
方法用于检查当前数组的容量是否足够。如果不够,则进行扩容。扩容策略通常是增加当前容量的一半(即1.5倍扩容)。System.arraycopy()
方法用于在数组之间复制数据,这在插入和删除操作中非常有用。
插入与删除:
add(int index, int num)
方法用于在指定位置插入元素。如果插入位置在数组中间,需要将后续元素向后移动。remove(int index)
方法用于删除指定位置的元素。删除后,需要将后续元素向前移动。
遍历方法:
foreach(Consumer<Integer> consumer)
方法使用Java的Consumer
接口实现对数组的遍历。iterator()
方法实现了Iterable
接口,允许使用增强for循环进行遍历。stream()
方法返回一个IntStream
,允许使用Java Stream API进行高级操作。
匿名内部类:
@Override
public Iterator<Integer> iterator() {
return new Iterator<Integer>() {
int i = 0;
@Override
public boolean hasNext() {
return i < size;
}
@Override
public Integer next() {
return arr[i++];
}
};
}
在这段代码中,
iterator()
方法返回一个匿名内部类实例,该实例实现了Iterator<Integer>
接口。匿名内部类的特点是没有类名,直接在创建对象时定义类的实现。这里通过匿名内部类实现了Iterator
接口的两个方法:
hasNext()
方法:检查是否还有下一个元素。next()
方法:返回当前元素,并将指针移动到下一个元素。匿名内部类的优点是简洁,适合用于一次性需求的场景。
System.arraycopy()
方法
Arrays.copyOfRange()
方法
public IntStream stream() {
return IntStream.of(Arrays.copyOfRange(arr, 0, size));
}
在这段代码中,
Arrays.copyOfRange(arr, 0, size)
方法用于生成一个包含有效元素的子数组。Arrays.copyOfRange()
方法的参数如下
original
:要复制的源数组。from
:要复制的范围的起始位置(包括)。to
:要复制的范围的结束位置(不包括)。- 该方法返回一个新的数组,包含源数组中从
from
到to
位置的元素。这里用于生成一个包含动态数组有效元素的子数组,供后续的流操作使用。
Lambda表达式
@Test
public void test1() {
DynamicArray dynamicArray = new DynamicArray();
dynamicArray.addlast(20);
dynamicArray.addlast(10);
dynamicArray.addlast(30);
dynamicArray.addlast(5);
dynamicArray.foreach(num -> {
System.out.println(num);
});
dynamicArray.sort();
}
在这段测试代码中,
dynamicArray.foreach(num -> { System.out.println(num); });
使用了Lambda表达式。Lambda表达式是Java 8引入的一种简洁的语法,用于表示匿名函数。这里的Lambda表达式num -> { System.out.println(num); }
表示一个接受一个整数参数num
并打印该参数的函数。Lambda表达式的基本语法是
(参数列表) -> { 函数体 }
。它可以使代码更加简洁和易读,特别适用于函数式接口(只包含一个抽象方法的接口)的实现。
总结
动态数组通过在运行时调整大小,提供了比传统数组更大的灵活性。通过合理使用Java的API,如System.arraycopy()
、Arrays.sort()
和IntStream
,可以高效地实现动态数组的各种操作。希望这篇博客能帮助你更好地理解动态数组的实现原理及其应用。