Skip to content

Commit 04eae87

Browse files
authored
refactor: DynamicArray (#5346)
1 parent 33fd79a commit 04eae87

File tree

2 files changed

+337
-105
lines changed

2 files changed

+337
-105
lines changed

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

+82-105
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
28+
* @throws IllegalArgumentException if the specified capacity is negative
2929
*/
3030
public DynamicArray(final int capacity) {
31+
if (capacity < 0) {
32+
throw new IllegalArgumentException("Capacity cannot be negative.");
33+
}
3134
this.size = 0;
32-
this.capacity = capacity;
33-
this.elements = new Object[this.capacity];
35+
this.modCount = 0;
36+
this.elements = new Object[capacity];
3437
}
3538

3639
/**
37-
* No-args constructor
40+
* No-args constructor with default capacity.
3841
*/
3942
public DynamicArray() {
4043
this(DEFAULT_CAPACITY);
4144
}
4245

4346
/**
44-
* Adds an element to the array If full, creates a copy array twice the size
45-
* of the current one
47+
* Adds an element to the array. If full, creates a new array with double the size.
4648
*
47-
* @param element the element of type <E> to be added to the array
49+
* @param element the element to be added to the array
4850
*/
4951
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++;
52+
ensureCapacity(size + 1);
53+
elements[size++] = element;
54+
modCount++; // Increment modification count
5655
}
5756

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

6876
/**
69-
* get method for element at a given index returns null if the index is
70-
* empty
77+
* Gets the element at a given index.
7178
*
7279
* @param index the desired index of the element
73-
* @return <E> the element at the specified index
80+
* @return the element at the specified index
81+
* @throws IndexOutOfBoundsException if n is less than 0 or greater or equal to the number of elements in the array
7482
*/
83+
@SuppressWarnings("unchecked")
7584
public E get(final int index) {
76-
return getElement(index);
85+
if (index < 0 || index >= size) {
86+
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
87+
}
88+
return (E) elements[index];
7789
}
7890

7991
/**
80-
* Removes an element from the array
92+
* Removes an element from the array.
8193
*
8294
* @param index the index of the element to be removed
83-
* @return <E> the element removed
95+
* @return the element removed
96+
* @throws IndexOutOfBoundsException if n is less than 0 or greater or equal to the number of elements in the array
8497
*/
8598
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));
99+
if (index < 0 || index >= size) {
100+
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
91101
}
102+
@SuppressWarnings("unchecked") 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;
126135
}
127136

128-
private E getElement(final int index) {
129-
return (E) this.elements[index];
130-
}
131-
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,50 @@ 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
}
201-
202-
/**
203-
* This class is the driver for the DynamicArray<E> class it tests a variety
204-
* of methods and prints the output
205-
*/
206-
public static void main(String[] args) {
207-
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);
216-
217-
System.out.println(names);
218-
219-
System.out.println(names.getSize());
220-
221-
names.remove(0);
222-
223-
for (String name : names) {
224-
System.out.println(name);
225-
}
226-
}
227204
}

0 commit comments

Comments
 (0)