Skip to content

Commit b8cefcb

Browse files
author
Alex Klymenko
committed
refactor: DynamicArray
1 parent 404ad72 commit b8cefcb

File tree

2 files changed

+346
-95
lines changed

2 files changed

+346
-95
lines changed

src/main/java/com/thealgorithms/datastructures/dynamicarray/DynamicArray.java

Lines changed: 87 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -10,145 +10,143 @@
1010
import java.util.stream.StreamSupport;
1111

1212
/**
13-
* This class implements a dynamic array
13+
* This class implements a dynamic array.
1414
*
1515
* @param <E> the type that each index of the array will hold
1616
*/
1717
public class DynamicArray<E> implements Iterable<E> {
1818

1919
private static final int DEFAULT_CAPACITY = 16;
20-
21-
private int capacity;
2220
private int size;
21+
private int modCount; // Tracks structural modifications for the iterator
2322
private Object[] elements;
2423

2524
/**
26-
* constructor
25+
* Constructor with initial capacity.
2726
*
2827
* @param capacity the starting length of the desired array
2928
*/
3029
public DynamicArray(final int capacity) {
30+
if (capacity < 0) {
31+
throw new IllegalArgumentException("Capacity cannot be negative.");
32+
}
3133
this.size = 0;
32-
this.capacity = capacity;
33-
this.elements = new Object[this.capacity];
34+
this.modCount = 0;
35+
this.elements = new Object[capacity];
3436
}
3537

3638
/**
37-
* No-args constructor
39+
* No-args constructor with default capacity.
3840
*/
3941
public DynamicArray() {
4042
this(DEFAULT_CAPACITY);
4143
}
4244

4345
/**
44-
* Adds an element to the array If full, creates a copy array twice the size
45-
* of the current one
46+
* Adds an element to the array. If full, creates a new array with double the size.
4647
*
47-
* @param element the element of type <E> to be added to the array
48+
* @param element the element to be added to the array
4849
*/
4950
public void add(final E element) {
50-
if (this.size == this.elements.length) {
51-
this.elements = Arrays.copyOf(this.elements, newCapacity(2 * this.capacity));
52-
}
53-
54-
this.elements[this.size] = element;
55-
size++;
51+
ensureCapacity(size + 1);
52+
elements[size++] = element;
53+
modCount++; // Increment modification count
5654
}
5755

5856
/**
59-
* Places element of type <E> at the desired index
57+
* Places an element at the desired index, expanding capacity if necessary.
6058
*
61-
* @param index the index for the element to be placed
59+
* @param index the index for the element to be placed
6260
* @param element the element to be inserted
61+
* @throws IndexOutOfBoundsException if n is less than 0 or greater or equal to the number of elements in the array
6362
*/
6463
public void put(final int index, E element) {
65-
this.elements[index] = element;
64+
if (index < 0) {
65+
throw new IndexOutOfBoundsException("Index cannot be negative.");
66+
}
67+
ensureCapacity(index + 1);
68+
elements[index] = element;
69+
if (index >= size) {
70+
size = index + 1;
71+
}
72+
modCount++; // Increment modification count
6673
}
6774

6875
/**
69-
* get method for element at a given index returns null if the index is
70-
* empty
76+
* Gets the element at a given index.
7177
*
7278
* @param index the desired index of the element
73-
* @return <E> the element at the specified index
79+
* @return the element at the specified index
80+
* @throws IndexOutOfBoundsException if n is less than 0 or greater or equal to the number of elements in the array
7481
*/
82+
@SuppressWarnings("unchecked")
7583
public E get(final int index) {
76-
return getElement(index);
84+
if (index < 0 || index >= size) {
85+
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
86+
}
87+
return (E) elements[index];
7788
}
7889

7990
/**
80-
* Removes an element from the array
91+
* Removes an element from the array.
8192
*
8293
* @param index the index of the element to be removed
83-
* @return <E> the element removed
94+
* @return the element removed
95+
* @throws IndexOutOfBoundsException if n is less than 0 or greater or equal to the number of elements in the array
8496
*/
8597
public E remove(final int index) {
86-
final E oldElement = getElement(index);
87-
fastRemove(this.elements, index);
88-
89-
if (this.capacity > DEFAULT_CAPACITY && size * 4 <= this.capacity) {
90-
this.elements = Arrays.copyOf(this.elements, newCapacity(this.capacity / 2));
98+
if (index < 0 || index >= size) {
99+
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
91100
}
101+
@SuppressWarnings("unchecked")
102+
E oldElement = (E) elements[index];
103+
fastRemove(index);
104+
modCount++; // Increment modification count
92105
return oldElement;
93106
}
94107

95108
/**
96-
* get method for size field
109+
* Gets the size of the array.
97110
*
98-
* @return int size
111+
* @return the size
99112
*/
100113
public int getSize() {
101-
return this.size;
114+
return size;
102115
}
103116

104117
/**
105-
* isEmpty helper method
118+
* Checks if the array is empty.
106119
*
107-
* @return boolean true if the array contains no elements, false otherwise
120+
* @return true if the array contains no elements, false otherwise
108121
*/
109122
public boolean isEmpty() {
110-
return this.size == 0;
123+
return size == 0;
111124
}
112125

113126
public Stream<E> stream() {
114127
return StreamSupport.stream(spliterator(), false);
115128
}
116129

117-
private void fastRemove(final Object[] elements, final int index) {
118-
final int newSize = this.size - 1;
119-
120-
if (newSize > index) {
121-
System.arraycopy(elements, index + 1, elements, index, newSize - index);
130+
private void ensureCapacity(int minCapacity) {
131+
if (minCapacity > elements.length) {
132+
int newCapacity = Math.max(elements.length * 2, minCapacity);
133+
elements = Arrays.copyOf(elements, newCapacity);
122134
}
123-
124-
this.size = newSize;
125-
this.elements[this.size] = null;
126-
}
127-
128-
private E getElement(final int index) {
129-
return (E) this.elements[index];
130135
}
131136

132-
private int newCapacity(int capacity) {
133-
this.capacity = capacity;
134-
return this.capacity;
137+
private void fastRemove(int index) {
138+
int numMoved = size - index - 1;
139+
if (numMoved > 0) {
140+
System.arraycopy(elements, index + 1, elements, index, numMoved);
141+
}
142+
elements[--size] = null; // Clear to let GC do its work
135143
}
136144

137-
/**
138-
* returns a String representation of this object
139-
*
140-
* @return String a String representing the array
141-
*/
142145
@Override
143146
public String toString() {
144-
return Arrays.toString(Arrays.stream(this.elements).filter(Objects::nonNull).toArray());
147+
return Arrays.toString(Arrays.copyOf(elements, size));
145148
}
146149

147-
/**
148-
* Creates and returns a new Dynamic Array Iterator
149-
*
150-
* @return Iterator a Dynamic Array Iterator
151-
*/
152150
@Override
153151
public Iterator<E> iterator() {
154152
return new DynamicArrayIterator();
@@ -157,71 +155,65 @@ public Iterator<E> iterator() {
157155
private final class DynamicArrayIterator implements Iterator<E> {
158156

159157
private int cursor;
158+
private int expectedModCount;
159+
160+
DynamicArrayIterator() {
161+
this.expectedModCount = modCount;
162+
}
160163

161164
@Override
162165
public boolean hasNext() {
163-
return this.cursor != size;
166+
checkForComodification();
167+
return cursor < size;
164168
}
165169

166170
@Override
171+
@SuppressWarnings("unchecked")
167172
public E next() {
168-
if (this.cursor > DynamicArray.this.size) {
173+
checkForComodification();
174+
if (cursor >= size) {
169175
throw new NoSuchElementException();
170176
}
171-
172-
if (this.cursor > DynamicArray.this.elements.length) {
173-
throw new ConcurrentModificationException();
174-
}
175-
176-
final E element = DynamicArray.this.getElement(this.cursor);
177-
this.cursor++;
178-
179-
return element;
177+
return (E) elements[cursor++];
180178
}
181179

182180
@Override
183181
public void remove() {
184-
if (this.cursor < 0) {
182+
if (cursor <= 0) {
185183
throw new IllegalStateException();
186184
}
185+
checkForComodification();
186+
DynamicArray.this.remove(--cursor);
187+
expectedModCount = ++modCount;
188+
}
187189

188-
DynamicArray.this.remove(this.cursor);
189-
this.cursor--;
190+
private void checkForComodification() {
191+
if (modCount != expectedModCount) {
192+
throw new ConcurrentModificationException();
193+
}
190194
}
191195

192196
@Override
193197
public void forEachRemaining(Consumer<? super E> action) {
194198
Objects.requireNonNull(action);
195-
196-
for (int i = 0; i < DynamicArray.this.size; i++) {
197-
action.accept(DynamicArray.this.getElement(i));
199+
while (hasNext()) {
200+
action.accept(next());
198201
}
199202
}
200203
}
201204

202-
/**
203-
* This class is the driver for the DynamicArray<E> class it tests a variety
204-
* of methods and prints the output
205-
*/
206205
public static void main(String[] args) {
207206
DynamicArray<String> names = new DynamicArray<>();
208-
names.add("Peubes");
209-
names.add("Marley");
210-
211-
for (String name : names) {
212-
System.out.println(name);
213-
}
214-
215-
names.stream().forEach(System.out::println);
207+
names.add("Alice");
208+
names.add("Bob");
216209

210+
names.forEach(System.out::println);
217211
System.out.println(names);
218212

219-
System.out.println(names.getSize());
213+
System.out.println("Size: " + names.getSize());
220214

221215
names.remove(0);
222216

223-
for (String name : names) {
224-
System.out.println(name);
225-
}
217+
names.forEach(System.out::println);
226218
}
227219
}

0 commit comments

Comments
 (0)