自制动态数组

前言:自制简陋动态数组,加深对原理的理解

(用stream去重非常非常好!!!)

1.add方法与toString方法

1
2
3
4
@Override
public String toString() {
return Arrays.toString(array);
}

ArrayList 类和简单数组都有其对应的 toString 实现,它们可以直接与 Arrays.toString() 配合使用。对于简单数组,Arrays.toString() 能够直接处理因为它被重载了多次以处理不同基本类型的数组(比如 int[], char[], double[] 等)以及对象数组(比如 String[], Integer[], Object[] 等)。
当你创建自己的动态数组类时,这个类是一个自定义的对象,不是数组类型。Arrays.toString() 方法不知道如何处理这个类的实例,因为它不是一个数组。它只能接受一个真正的数组作为参数。
如果你的动态数组类内部使用了一个原生数组来存储元素,你需要在类的 toString() 方法中调用 Arrays.toString() 并传入那个内部数组。
当你调用 System.out.println(instance);,Java 会自动调用你为你的动态数组类提供的 toString() 方法,而该方法会返回 Arrays.toString(array) 的结果,其中 array 是你的内部存储结构。这样就可以打印出动态数组的内容了

2.size为0时第一次添加index为0怎么办?怎么处理这个逻辑?看看源码别人是怎么写的。

1
2
3
4
5
6
7
8
9
10
public void add(int index, int element) {
if (index >= 0 && index < size) {
System.arraycopy(array, index, array, index + 1, size - index);
array[index] = element;
size++;
} else if (index==size) {

} else
System.out.println("ArrayIndexOutOfBoundsException!");
//此处有一些终止程序运行的逻辑

ArrayList:利用空参创建集合,在底层创建一个默认长度为0的数组,添加第一个元素时,底层会创建一个长度为10的数组。这就很好的解决了问题!

size的两个含义:元素个数和下次应存的位置。

3.很好的遍历方式forEach

1
2
3
4
5
6
//很好的遍历方式,这样遍历时做什么就可以直接由外部决定,较灵活
public void forEach(Consumer<Integer> consumer){
for (int i = 0; i < size; i++) {
consumer.accept(array[i]);
}
}

它使用了 Java 的函数式编程特性。方法 forEach 接受一个 Consumer<Integer> 类型的参数,这个参数是一个函数式接口,代表了接受单个输入参数并且不返回结果的操作。在这个方法中,Consumer<Integer> 接口用来对整数进行操作。外部则可以使用lambda表达式进行想要的操作。
函数式编程的第一个特点就是可以把函数作为参数传递给另一个函数,也就是所谓的高阶函数。例如,对数组进行排序,可以传入一个排序函数作为参数:

1
2
String[] array = { "orange", "Pear", "Apple" };
Arrays.sort(array, String::compareToIgnoreCase);

函数式编程的第二个特点就是可以返回一个函数,这样就可以实现闭包或者惰性计算:

以上两个特点还仅仅是简化了代码。从代码的可维护性上讲,函数式编程最大的好处是引用透明,即函数运行的结果只依赖于输入的参数,而不依赖于外部状态,因此,我们常常说函数式编程没有副作用。

没有副作用有个巨大的好处,就是函数内部无状态,即输入确定,输出就是确定的,容易测试和维护。

4.迭代器遍历

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@Override
public Iterator<Integer> iterator() {
//因为Iterator是接口,所以需要new一个实例对象
return new Iterator<Integer>() {
int i=0;
@Override
public boolean hasNext() {
return i<size;
}
@Override
public Integer next() {
return array[i++];//返回当前元素之后移动到下一个元素
}
};
}

5.流(超棒的)

1
2
3
4
5
//流的实现
public IntStream stream(){
//只将有效部分传入
return IntStream.of(Arrays.copyOfRange(array,0,size));
}

6.完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package Demo5;

import java.util.Arrays;
import java.util.Iterator;
import java.util.function.Consumer;
import java.util.stream.IntStream;

public class DynamicArray implements Iterable<Integer> {
private int size;//大小
private int capacity;//容量
private int[] array;

public DynamicArray() {
array = null;
size = 0;
}
//抽取检查索引方法
public void rangeCheck(int index){
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException("ArrayIndexOutOfBoundsException");
}

//很重要的扩容
public void ensureCapacity(int minCapacity) {
//如果添加第一个元素,则初始化数组
if (array == null)
array = new int[Math.max(minCapacity, 10)];//很好的比较
else if (array.length < minCapacity) {
//普通情况翻倍
int newCapacity = array.length * 2;
//还是不足则扩至需要容量
if (newCapacity < minCapacity)
newCapacity = minCapacity;
//创建并返回一个新的数组
array = Arrays.copyOf(array, newCapacity);
}
}

public void add(int index, int element) {
//检查索引是否有效(这个别抽取)
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("ArrayIndexOutOfBoundsException");
//确保所需最小容量足够,否则扩容
ensureCapacity(size + 1);
//若在中间插入需要多做一步
if (index < size)
System.arraycopy(array, index, array, index + 1, size - index);
//将中间或末尾插入的逻辑合在一块
array[index] = element;
size++;
}
//删除指定索引的元素并且返回删除的元素值
public int remove(int index){
rangeCheck(index);
int removed=array[index];
if(index<size-1)
System.arraycopy(array,index+1,array,index,size-index-1);
size--;
return removed;
}
//forEach遍历方式(避免与iterator中的forEach重复改成小写)
public void foreach(Consumer<Integer> consumer){
for (int i = 0; i < size; i++) {
consumer.accept(array[i]);
}
}
//迭代器遍历
@Override
public Iterator<Integer> iterator() {
//因为Iterator是接口,所以需要new一个实例对象
return new Iterator<Integer>() {
int i=0;
@Override
public boolean hasNext() {
return i<size;
}
@Override
public Integer next() {
return array[i++];//返回当前元素之后移动到下一个元素
}
};
}
//流的实现
public IntStream stream(){
//只将有效部分传入
return IntStream.of(Arrays.copyOfRange(array,0,size));
}
public int get(int index) {
rangeCheck(index);
return array[index];
}

public int size() {
return size;
}

public boolean isEmpty() {
return size == 0;
}
//打印
@Override
public String toString() {
return Arrays.toString(Arrays.copyOf(array,size));
}
}