数据结构——SimpleArrayList和SimpleLinkedList的实现

SimpleArrayList为用数组存储数据元素的方式实现的表,它是在SimpleList的基础上改进的。SimpleLinkedList采取的是双链表的实现方式。

SimpleArrayList

数组实现的表的特性:

  1. 可以非常快的随机访问表中第x个元素的值,所以当我们需要比较频繁的对数据进行随机访问存取的时候,这不失为一个很好的选择。

  2. 但是它的缺点也很明显,就是对于元素的插入和删除的花销都是线性增长的。因为无论是插入还是删除都需要涉及到元素的移位操作。其最坏的结果就是要在表的头部插入或删除元素,这需要把所有的元素都进行移位操作。

  3. 因为使用数组存储的方式,如果要实现可动态扩展数组大小的话,就需要一些额外的花销。当插入元素的时候发现数组大小不够用了的时候就要对数组进行扩展,同时还要拷贝旧数组中的元素到新的数组中。

  在这个SimpleArrayList中将实现Iterabel接口,但是没有实现当元素被修改时迭代器也失效的效果,这个留到SimpeLinkedList中实现。

属性

  1. private int[] elements : 数组元素
  2. private int size : 表中元素个数
  3. private final static int DEFAULT_CAPACITY : 默认表大小

构造器

  有两个构造器,一个是无参的构造器,将构造一个默认大小的表;另一个则是可以初始化表容量大小的构造器。

1
2
3
4
5
6
7
8
9
10
public SimpleArrayList(){
clear();
}
public SimpleArrayList(int capacity) throws IllegalArgumentException{
if(capacity < 0)
throw new IllegalArgumentException("Capacity should be positive number.");
size = 0;
ensureCapacity(capacity);
}

方法

  SimpleArrayList中大部分方法实现和SimpleList很类似,就不赘述了。

  ensureCapacity(): 扩展数组的大小,并且拷贝原数组中的元素。在这个函数中只有当新设置的容器大小至少等于当前容器大小的时候才有效,否则将忽略这个请求。

1
2
3
4
5
6
7
8
9
10
public void ensureCapacity(int newCapacity){
/* 判断新表大小是否大于等于当前容器中元素个数 */
if(newCapacity < size())
return;
T[] old = elements;
/* 创建新数组并拷贝旧数组元素 */
elements = (T[]) new Object[newCapacity];
for(int i=0; i < size(); i++)
elements[i] = old[i];
}

  trimToSize():本函数可以释放数组中多余的空间,将数组的大小设置为当前表中元素个数。当我们已经确定了表的大小几乎不用发生改变了的时候可以进行该操作释放空间资源。

1
2
3
public void trimToSize(){
ensureCapacity(size());
}

  add():为插入元素的函数。 当数组大小不够用时会扩展数组。
  append():为在尾部追加元素的操作,这个操作不涉及元素的移位。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void add(int idx, T ele) throws ArrayIndexOutOfBoundsException{
if(idx < 0 || idx > size())
throw new ArrayIndexOutOfBoundsException("Idx is out of bounds.");
/* 判断表是否已满,实则进行扩容操作 */
if(size() == elements.length)
ensureCapacity(2 * size() + 1);
/* 移动idx后的所有元素 */
for(int i=size(); i > idx; i--)
elements[i] = elements[i-1];
/* 设置插入的元素值 */
elements[idx] = ele;
size++;
}
public void append(T ele){
add(size(), ele);
}

  remove():移除元素。

1
2
3
4
5
6
7
8
9
10
11
public T remove(int idx) throws ArrayIndexOutOfBoundsException{
if(idx < 0 || idx >= size())
throw new ArrayIndexOutOfBoundsException("Idx is out of bounds.");
/* 保存旧元素值 */
T removeElement = elements[idx];
/* idx后面的元素向前移动一位 */
for(int i=idx; i<size()-1; i++)
elements[i] = elements[i+1];
size--;
return removeElement;
}

  实现了Iterable接口的方法,返回一个Iterator对象。我自己实现了一个SimpleArrayListIterator类来实现迭代器。

1
2
3
4
@Override
public Iterator<T> iterator() {
return new SimpleArrayListIterator();
}

内部类(SimpleArrayListIterator)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private class SimpleArrayListIterator implements Iterator<T>{
private int current = 0;
@Override
public boolean hasNext() {
return current != size()? true : false;
}
@Override
public T next() {
/* 判断是否到尾部,即其后还有没有元素 */
if(!hasNext())
throw new NoSuchElementException("Has no next object.");
return elements[current++];
}
@Override
public void remove(){
/* 前置减,先计算后取值,所以删除的是刚调用next()后所指向的元素 */
SimpleArrayList.this.remove(--current);
}
}

SimpleLinkedList

链式实现的表的结构:

  1. 链表不能进行元素的随机访问,每次需要访问某个特定元素的时候都要从表头会表尾巴开始查找(双链表可以在表尾开始查找元素)。所以这些操作的花销都是线性增长的,并不鼓励对链表进行频繁的get, set操作。

  2. 链表最大的优点就是对于已知位置的插入和删除操作是非常快的,只需要常数时间就可以完成,而不需要对任何元素进行移位操作。链表适合于要对表频繁的进行插入和删除操作的情况。

  3. 链表采用的链式结构,节点都是非连续的存储在内存中的,只要内存空间充足,表的大小就可以看作是无限的。所以不需要额外的调整表大小的花销。

  SimpleLinkedList采用了双链表的实现方式,并且在SimpleArrayList的基础上,不但实现了Iterable接口,还通过新增一个modCount变量来实现迭代器失效的效果。

成员变量

  1. private int size:表元素个数;
  2. private int modCount:修改记录数,迭代器用来判断迭代期间是否发生修改;
  3. private Node<T> head:指向头节点的引用;
  4. private Node<T> tail:指向尾结点的引用。

构造器

1
2
3
public SimpleLinkedList(){
clear();
}

函数

  clear():重置链表为空表。

1
2
3
4
5
6
7
public void clear(){
head = new Node<T>(null, null, null);
tail = new Node<T>(null, head, null);
head.next = tail;
size = 0;
modCount++;
}

  获取表中元素个数和判断是否是空表。

1
2
3
4
5
6
7
8
public int size(){
return size;
}
public boolean isEmpty(){
return size() == 0;
//return head.next == tail;
}

  getNode():在链表中定位指定位置的元素,这里先判断要定位的元素处于元素前半部分还是后半部分,然后根据元素所在位置选择在头部还是尾部开始定位元素。

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
/**
* 定位到索引idx所表示的结点,idx取值范围是0~size
* @param idx 节点索引位置
* @return 返回idx位置的节点对象
* @throws IndexOutOfBoundsException
*/
public Node<T> getNode(int idx) throws IndexOutOfBoundsException{
/* 判断idx是否越界,是则抛出异常 */
if(idx < 0 || idx > size())
throw new IndexOutOfBoundsException();
Node<T> p;
/* 如果要定位的元素在前半部分,则从前半部分开始定位s */
if(idx < size()/2){
p = head.next;
for(int i=0; i < idx; i++)
p = p.next;
}
/* 元素在后半部分,则从尾部开始定位 */
else{
p = tail;
for(int i = size(); i > idx; i--)
p = p.prev;
}
return p;
}

  append():尾部追加元素。
  add():插入元素。
  addBefore():私有方法,在节点p的前面插入元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public boolean append(T data){
add(size(), data);
return true;
}
public void add(int idx, T data) throws IndexOutOfBoundsException{
addBefore(getNode(idx), data);
}
private void addBefore(Node<T> p, T data){
Node<T> newOne = new Node<T>(data, p.prev, p);
p.prev.next = newOne;
p.prev = newOne;
size++;
modCount++;
}

  removeFirst():删除第一个节点。
  removeLast():删除最后一个节点。
  remove():删除指定位置的节点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public T removeFirst(){
return remove(getNode(0));
}
public T removeLast(){
return remove(size()-1);
}
public T remove(int idx) throws IndexOutOfBoundsException{
return remove(getNode(idx));
}
private T remove(Node<T> p) throws IndexOutOfBoundsException{
p.prev.next = p.next;
p.next.prev = p.prev;
size--;
modCount++;
return p.data;
}

  get():获取指定位置节点的元素值。
  set():给指定位置的节点设置新的元素值。

1
2
3
4
5
6
7
8
9
10
11
public T get(int idx) throws IndexOutOfBoundsException{
return getNode(idx).data;
}
public T set(int idx, T newData) throws IndexOutOfBoundsException{
Node<T> p = getNode(idx);
T oldData = p.data;
p.data = newData;
modCount++;
return oldData;
}

  实现了Iterable接口的方法,返回一个Iterator对象。我自己实现了一个SimpleLinkedListIterator类来实现迭代器。

1
2
3
4
@Override
public Iterator<T> iterator() {
return new SimpleLinkedListIterator();
}

内部类

1
2
3
4
5
6
7
8
9
10
11
private static class Node<T>{
public T data;
public Node<T> prev;
public Node<T> next;
public Node(T data, Node<T> prev, Node<T> next){
this.data = data;
this.prev = prev;
this.next = next;
}
}

SimpleLinkedListIterator

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
private class SimpleLinkedListIterator implements Iterator<T>{
private Node<T> current = head.next;
private int ExpectedModCount = modCount;
private boolean okToRemove = false;
@Override
public boolean hasNext() {
return current != tail ? true : false;
}
@Override
public T next() {
if(ExpectedModCount != modCount)
throw new ConcurrentModificationException();
if(!hasNext())
throw new NoSuchElementException();
T data = current.data;
current = current.next;
okToRemove = true;
return data;
}
@Override
public void remove(){
if(ExpectedModCount != modCount)
throw new ConcurrentModificationException();
if(!okToRemove)
throw new IllegalStateException();
SimpleLinkedList.this.remove(current.prev);
okToRemove = false;
ExpectedModCount++;
}
}

源代码

SimpleArrayList.java

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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* 一个简单的ArrayList的实现,暂且忽略了collection等接口
* 只实现了Iterator接口,其返回一个自己实现的内部类SimpleArrayListIterator
* @author Bingo
*
*/
public class SimpleArrayList<T> implements Iterable<T> {
/**
* 表的默认大小,当表中元素即将满时会扩展
*/
public static int DEFAULT_CAPACITY;
private int size = 0;
private T[] elements;
/**
* 无参构造器,创建一个默认大小的表
*/
public SimpleArrayList(){
clear();
}
/**
* 构造一个自定义初始大小的构造器
*/
public SimpleArrayList(int capacity) throws IllegalArgumentException{
if(capacity < 0)
throw new IllegalArgumentException("Capacity should be positive number.");
size = 0;
ensureCapacity(capacity);
}
/**
* 返回容器中元素个数
* @return
*/
public int size(){
return size;
}
/**
* 判断表是否为空表
* @return
*/
public boolean isEmpty(){
return size() == 0;
}
/**
* 重置为空表
*/
public void clear(){
size = 0;
ensureCapacity(DEFAULT_CAPACITY);
}
/**
* 重置表大小为容器中元素个数,释放多余的空间.
*/
public void trimToSize(){
ensureCapacity(size());
}
/**
* 重置容器大小,可用于重置容器或者扩充容器大小.
* 新容器大小要大于等于当前容器大小才有效,否则忽略.
* @param newCapacity 新容器大小
*/
@SuppressWarnings("unchecked")
public void ensureCapacity(int newCapacity){
/* 判断新表大小是否大于等于当前容器中元素个数 */
if(newCapacity < size())
return;
T[] old = elements;
/* 创建新数组并拷贝旧数组元素 */
elements = (T[]) new Object[newCapacity];
for(int i=0; i < size(); i++)
elements[i] = old[i];
}
/**
* 获取索引为idx的元素的值,idx取值为0~(size()-1)
* @param idx 元素索引
* @return 返回元素值
* @throws ArrayIndexOutOfBoundsException 当idx超出表元素个数范围时抛出异常.
*/
public T get(int idx) throws ArrayIndexOutOfBoundsException{
if(idx < 0 || idx >= size())
throw new ArrayIndexOutOfBoundsException("Idx is out of bounds.");
return elements[idx];
}
/**
* 给索引为idx的元素设置新的元素值,idx取值为0~(size()-1)
* @param idx 元素索引
* @param ele 元素新的值
* @return 旧的元素值
* @throws ArrayIndexOutOfBoundsException 当idx超出表元素个数范围时抛出异常.
*/
public T set(int idx, T ele) throws ArrayIndexOutOfBoundsException{
if(idx < 0 || idx >= size())
throw new ArrayIndexOutOfBoundsException("Idx is out of bounds.");
T old = elements[idx];
elements[idx] = ele;
return old;
}
/**
* 插入新的元素
* idx的取值范围为0-size(),idx为0-(size()-1)时即位在对应索引的元素前面插入元素,
* idx为size()时,则是在尾部插入元素.
* @param idx 插入的位置索引
* @param ele 新的元素值
* @throws ArrayIndexOutOfBoundsException 当idx超出范围时抛出异常.
*/
public void add(int idx, T ele) throws ArrayIndexOutOfBoundsException{
if(idx < 0 || idx > size())
throw new ArrayIndexOutOfBoundsException("Idx is out of bounds.");
/* 判断表是否已满,实则进行扩容操作 */
if(size() == elements.length)
ensureCapacity(2 * size() + 1);
/* 移动idx后的所有元素 */
for(int i=size(); i > idx; i--)
elements[i] = elements[i-1];
/* 设置插入的元素值 */
elements[idx] = ele;
size++;
}
/**
* 在尾部追加新的元素
* @param ele 新的元素值
*/
public void append(T ele){
add(size(), ele);
}
/**
* 移除索引idx所代表的元素, idx取值范围为
* @param idx 索引位置
* @return 返回被删除的元素值
* @throws ArrayIndexOutOfBoundsException 当idx超出范围时抛出异常.
*/
public T remove(int idx) throws ArrayIndexOutOfBoundsException{
if(idx < 0 || idx >= size())
throw new ArrayIndexOutOfBoundsException("Idx is out of bounds.");
/* 保存旧元素值 */
T removeElement = elements[idx];
/* idx后面的元素向前移动一位 */
for(int i=idx; i<size()-1; i++)
elements[i] = elements[i+1];
size--;
return removeElement;
}
/**
* 返回一个iterator对象,其是用内部类实现的一个SimpleArrayListIterator.
*/
@Override
public Iterator<T> iterator() {
return new SimpleArrayListIterator();
}
private class SimpleArrayListIterator implements Iterator<T>{
private int current = 0;
@Override
public boolean hasNext() {
return current != size()? true : false;
}
@Override
public T next() {
/* 判断是否到尾部,即其后还有没有元素 */
if(!hasNext())
throw new NoSuchElementException("Has no next object.");
return elements[current++];
}
@Override
public void remove(){
/* 前置减,先计算后取值,所以删除的是刚调用next()后所指向的元素 */
SimpleArrayList.this.remove(--current);
}
}
@Override
public String toString() {
StringBuffer sb = new StringBuffer();
sb.append("SimpleArrayList [");
for(int i=0; i < size(); i++)
sb.append(elements[i] + ",");
sb = sb.delete(sb.length()-1, sb.length());
sb.append("] size = " + size());
return sb.toString();
}
}

SimpleLinkedList.java

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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* 一个简单LinkedList的实现,采用的是双向链表形式,并且表中维护一个头节点和一个尾节点。
*
* @author Bingo
*
* @param <T>
*/
public class SimpleLinkedList<T> implements Iterable<T> {
private int size;
/**
* 一个记录修改次数的变量,用于迭代器中进行判断迭代期间是否发生修改.
*/
private int modCount = 0;
/**
* 头节点,next域指向第一个结点元素
*/
private Node<T> head;
/**
* 尾节点,prev指向最后一个结点元素
*/
private Node<T> tail;
/**
* 构造器
*/
public SimpleLinkedList(){
clear();
}
/**
* 重置表为空表
*/
public void clear(){
head = new Node<T>(null, null, null);
tail = new Node<T>(null, head, null);
head.next = tail;
size = 0;
modCount++;
}
/**
* 查询表中元素个数
* @return
*/
public int size(){
return size;
}
/**
* 判断是否为空表
* @return
*/
public boolean isEmpty(){
return size() == 0;
//return head.next == tail;
}
/**
* 定位到索引idx所表示的结点,idx取值范围是0~size
* @param idx 节点索引位置
* @return 返回idx位置的节点对象
* @throws IndexOutOfBoundsException
*/
public Node<T> getNode(int idx) throws IndexOutOfBoundsException{
/* 判断idx是否越界,是则抛出异常 */
if(idx < 0 || idx > size())
throw new IndexOutOfBoundsException();
Node<T> p;
/* 如果要定位的元素在前半部分,则从前半部分开始定位s */
if(idx < size()/2){
p = head.next;
for(int i=0; i < idx; i++)
p = p.next;
}
/* 元素在后半部分,则从尾部开始定位 */
else{
p = tail;
for(int i = size(); i > idx; i--)
p = p.prev;
}
return p;
}
/**
* 在尾部追加元素
* @param data 新增元素值
* @return 添加成功返回true
*/
public boolean append(T data){
add(size(), data);
return true;
}
/**
* 在指定位置idx处的前部添加元素,idx取值范围为0~size
* @param idx 待插入元素的位置idx
* @param data 新的元素值
* @throws IndexOutOfBoundsException
*/
public void add(int idx, T data) throws IndexOutOfBoundsException{
addBefore(getNode(idx), data);
}
/**
* 在节点p的前部插入新的元素
* @param p 带插入值的结点
* @param data 新的元素值
*/
private void addBefore(Node<T> p, T data){
Node<T> newOne = new Node<T>(data, p.prev, p);
p.prev.next = newOne;
p.prev = newOne;
size++;
modCount++;
}
/**
* 删除第一个元素
* @return 返回第一个元素的值
*/
public T removeFirst(){
return remove(getNode(0));
}
/**
* 删除最后一个元素
* @return 返回最后一个元素的值
*/
public T removeLast(){
return remove(size()-1);
}
/**
* 删除位置idx所表示的节点对象
* @param idx 结点索引
* @return 返回被删除的结点p的值
* @throws IndexOutOfBoundsException
*/
public T remove(int idx) throws IndexOutOfBoundsException{
return remove(getNode(idx));
}
/**
* 删除结点p
* @param p
* @return 返回被删除的结点p的值
* @throws IndexOutOfBoundsException
*/
private T remove(Node<T> p) throws IndexOutOfBoundsException{
p.prev.next = p.next;
p.next.prev = p.prev;
size--;
modCount++;
return p.data;
}
/**
* 获取第idx位元素的值
* @param idx 索引位置
* @return 返回查找到的元素值
* @throws IndexOutOfBoundsException
*/
public T get(int idx) throws IndexOutOfBoundsException{
return getNode(idx).data;
}
/**
* 设置第idx位元素的值为新的元素值
* @param idx 索引位置
* @param newData 新的元素值
* @return 返回旧的元素值
* @throws IndexOutOfBoundsException
*/
public T set(int idx, T newData) throws IndexOutOfBoundsException{
Node<T> p = getNode(idx);
T oldData = p.data;
p.data = newData;
modCount++;
return oldData;
}
/**
* 实现iterable接口的方法,返回一个自定义的SimpleLinkedList对象
*/
@Override
public Iterator<T> iterator() {
return new SimpleLinkedListIterator();
}
/**
* 私有节点类,保存数据data,并且包括指向前驱结点和后继结点的引用.
* 本类设置为私有类,且所以成员属性都设置为共有的.即使如此,也只有外部类可以直接访问该类中的成员变量,
* 方便外部类中代码的实现.
* @author Bingo
*
* @param <T>
*/
private static class Node<T>{
public T data;
public Node<T> prev;
public Node<T> next;
public Node(T data, Node<T> prev, Node<T> next){
this.data = data;
this.prev = prev;
this.next = next;
}
}
/**
* 实现了iterator接口的类.
* @author Bingo
*
*/
private class SimpleLinkedListIterator implements Iterator<T>{
private Node<T> current = head.next;
private int ExpectedModCount = modCount;
private boolean okToRemove = false;
@Override
public boolean hasNext() {
return current != tail ? true : false;
}
@Override
public T next() {
if(ExpectedModCount != modCount)
throw new ConcurrentModificationException();
if(!hasNext())
throw new NoSuchElementException();
T data = current.data;
current = current.next;
okToRemove = true;
return data;
}
@Override
public void remove(){
if(ExpectedModCount != modCount)
throw new ConcurrentModificationException();
if(!okToRemove)
throw new IllegalStateException();
SimpleLinkedList.this.remove(current.prev);
okToRemove = false;
ExpectedModCount++;
}
}
}