Skip to content

Commit 60527e9

Browse files
committed
added test cases
1 parent 93f708c commit 60527e9

13 files changed

Lines changed: 1114 additions & 318 deletions

key.util/src/main/java/org/key_project/util/collection/ImmutableList.java

Lines changed: 131 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,21 @@
33
* SPDX-License-Identifier: GPL-2.0-only */
44
package org.key_project.util.collection;
55

6+
import org.jspecify.annotations.Nullable;
7+
8+
import java.lang.reflect.Array;
69
import java.util.*;
710
import java.util.function.Function;
811
import java.util.function.Predicate;
912
import java.util.stream.Collector;
13+
import java.util.stream.Collectors;
1014
import java.util.stream.Stream;
1115
import java.util.stream.StreamSupport;
1216

13-
import org.jspecify.annotations.Nullable;
14-
1517
/**
1618
* List interface to be implemented by non-destructive lists
1719
*/
20+
@SuppressWarnings("unchecked")
1821
public interface ImmutableList<T extends @Nullable Object>
1922
extends Iterable<T>, java.io.Serializable {
2023

@@ -31,14 +34,28 @@ public interface ImmutableList<T extends @Nullable Object>
3134
}, ImmutableList::fromList);
3235
}
3336

34-
/**
35-
* Creates an ImmutableList from a List.
36-
*
37-
* @param list a List.
38-
* @return an ImmutableList containing the same elements as the specified list.
39-
*/
37+
/// Creates an [ImmutableList] from an [Iterable].
38+
/// This method is not recommended to use as space overhead is required.
39+
///
40+
/// @param list a List.
41+
/// @return an ImmutableList containing the same elements as the specified list.
42+
/// @see #fromList(List)
43+
/// @see #fromArray(Object[])
4044
static <T extends @Nullable Object> ImmutableList<T> fromList(Iterable<? extends T> list) {
41-
return new ImmutableListList<>(list);
45+
// Short-cuts, iterables are often lists, if so use them and try to avoid copy.
46+
if (list instanceof ImmutableList<?> ilist) {
47+
return (ImmutableList<T>) ilist;
48+
}
49+
50+
if (list instanceof List<?> ilist) {
51+
return fromList((List<T>) ilist);
52+
}
53+
54+
List<T> seq = new ArrayList<>(64);
55+
for (var t : list) {
56+
seq.add(t);
57+
}
58+
return fromList(seq);
4259
}
4360

4461
static <T extends @Nullable Object> ImmutableList<T> fromList(List<T> list) {
@@ -53,8 +70,8 @@ public interface ImmutableList<T extends @Nullable Object>
5370
/**
5471
* Return an empty immutable list.
5572
*
56-
* @return empty immutable list.
5773
* @param <T> the entry type of the list.
74+
* @return empty immutable list.
5875
*/
5976
static <T extends @Nullable Object> ImmutableList<T> of() {
6077
return nil();
@@ -63,66 +80,51 @@ public interface ImmutableList<T extends @Nullable Object>
6380
/**
6481
* Return a singleton immutable list.
6582
*
66-
* @param e1 the element to put into the list
67-
* @return singleton immutable list.
83+
* @param e1 the element to put into the list
6884
* @param <T> the entry type of the list.
85+
* @return singleton immutable list.
6986
*/
7087
static <T extends @Nullable Object> ImmutableList<T> of(T e1) {
7188
return singleton(e1);
7289
}
7390

74-
/**
75-
* Return an immutable list with two elements.
76-
* The iteration order is: e1 then e2
77-
*
78-
* @param e1 the element to put into the list
79-
* @param e2 the element to put into the list
80-
* @return (e1, e2) as immutable list
81-
* @param <T> the entry type of the list.
82-
*/
83-
static <T extends @Nullable Object> ImmutableList<T> of(T e1, T e2) {
84-
return singleton(e2).prepend(e1);
85-
}
86-
87-
/**
88-
* Return an immutable list with three elements.
89-
* The iteration order is: e1 then e2 then e3
90-
*
91-
* @param e1 the element to put into the list
92-
* @param e2 the element to put into the list
93-
* @param e3 the element to put into the list
94-
* @return (e1, e2, e3) as immutable list
95-
* @param <T> the entry type of the list.
96-
*/
97-
static <T extends @Nullable Object> ImmutableList<T> of(T e1, T e2, T e3) {
98-
return singleton(e3).prepend(e2).prepend(e1);
99-
}
100-
10191
/**
10292
* Return an immutable list with the iterated elements.
10393
* The iteration order is the order of the arguments
10494
*
105-
* @param es the elements to put into the list
106-
* @return (e1, e2, e3, ...) as immutable list
95+
* @param es the elements to put into the list
10796
* @param <T> the entry type of the list.
97+
* @return (e1, e2, e3, ...) as immutable list
10898
*/
10999
static <T extends @Nullable Object> ImmutableList<T> of(T... es) {
100+
if (es.length == 0) {
101+
return of();
102+
}
103+
return new ImmutableListArray<>(es);
104+
105+
/*
110106
ImmutableList<T> result = nil();
111107
for (int i = es.length - 1; i >= 0; i--) {
112108
result = result.prepend(es[i]);
113109
}
114-
return result;
110+
return result;*/
115111
}
116112

117-
/** the empty list */
118-
static <T extends @Nullable Object> ImmutableSLList<T> nil() {
113+
/**
114+
* the empty list
115+
*/
116+
static <T extends @Nullable Object> ImmutableList<T> nil() {
119117
return (ImmutableSLList<T>) ImmutableSLList.NIL.NIL;
120118
}
121119

122-
static <T extends @Nullable Object> ImmutableSLList<T> singleton(T obj) {
120+
static <T extends @Nullable Object> ImmutableList<T> singleton(T obj) {
123121
return new ImmutableSLList.Cons<>(obj, nil());
124122
}
125123

124+
static <T extends @Nullable Object> String toString(ImmutableList<T> seq) {
125+
return seq.stream().map(Objects::toString).collect(Collectors.joining(", ", "[", "]"));
126+
}
127+
126128
default ImmutableList<T> prepend(T element) {
127129
return new ImmutableListConcat<>(ImmutableList.singleton(element), this);
128130
}
@@ -168,11 +170,18 @@ default T head() {
168170

169171
/**
170172
* return true if predicate is fullfilled for at least one element
173+
* <p>
174+
* The default implementation is based on the {@link #stream()} functionality
171175
*
172176
* @param predicate the predicate
173177
* @return true if predicate is fullfilled for at least one element
178+
* @see #stream()
179+
* @see #spliterator()
180+
*
174181
*/
175-
boolean exists(Predicate<? super T> predicate);
182+
default boolean exists(Predicate<? super T> predicate) {
183+
return stream().anyMatch(predicate);
184+
}
176185

177186
/**
178187
* @return IList<T> tail of list
@@ -190,6 +199,9 @@ default ImmutableList<T> skip(int n) {
190199
* @return IList<T> this list without the first <code>n</code> elements
191200
*/
192201
default ImmutableList<T> take(int n) {
202+
if (n == 0) {
203+
return nil();
204+
}
193205
return new ImmutableListSubList<>(this, 0, n);
194206
}
195207

@@ -204,7 +216,21 @@ default ImmutableList<T> reverse() {
204216
* @return Iterator<T> of this list
205217
*/
206218
@Override
207-
Iterator<T> iterator();
219+
default Iterator<T> iterator() {
220+
return new Iterator<>() {
221+
int cursor = 0;
222+
223+
@Override
224+
public boolean hasNext() {
225+
return cursor < size();
226+
}
227+
228+
@Override
229+
public T next() {
230+
return get(cursor++);
231+
}
232+
};
233+
}
208234

209235
/**
210236
* @return boolean is true iff. obj is in List
@@ -239,21 +265,49 @@ default boolean contains(@Nullable Object obj) {
239265
*/
240266
ImmutableList<T> removeAll(T obj);
241267

242-
/**
243-
* Convert the list to a Java array (O(n))
244-
*/
245-
<S extends @Nullable Object> S[] toArray(S[] array);
268+
/// Convert the list to a Java array (O(n)) based on the {@link #iterator()} implementation.
269+
/// If you can convert faster, feel free to override.
270+
///
271+
/// @see #iterator()
272+
@SuppressWarnings("unchecked")
273+
default <S extends @Nullable Object> S[] toArray(S[] array) {
274+
S[] result;
275+
if (array.length < size()) {
276+
Class<? extends Object[]> arrayClass = array.getClass();
277+
assert arrayClass.isArray()
278+
: "@AssumeAssertion(nullness): This has indeed a component type";
279+
result = (S[]) Array.newInstance(arrayClass.getComponentType(), size());
280+
} else {
281+
result = array;
282+
}
246283

247-
/**
248-
* Convert the list to a Java array (O(n))
249-
*/
250-
<S extends @Nullable Object> S[] toArray(Class<S> type);
284+
int pos = 0;
285+
for (var item : this) {
286+
result[pos++] = (S) item;
287+
}
288+
return result;
289+
}
290+
291+
/// Convert the list to a Java array (O(n)) of the given class `type`.
292+
///
293+
/// @see #iterator()
294+
default <S extends @Nullable Object> S[] toArray(Class<S> type) {
295+
S[] result = (S[]) Array.newInstance(type, size());
296+
int pos = 0;
297+
for (var head : this) {
298+
// Somehow the nullness checker needs this cast to be explicit.
299+
result[pos++] = (S) type.cast(head);
300+
}
301+
return result;
302+
}
251303

252304

253305
/**
254306
* A stream object for this collection.
255307
*
256308
* @return a non-null stream object
309+
* @see #spliterator()
310+
* @see #iterator()
257311
*/
258312
default Stream<T> stream() {
259313
return StreamSupport.stream(this.spliterator(), false);
@@ -277,9 +331,8 @@ default List<T> toList() {
277331
* the given predicate.
278332
*
279333
* @param predicate a non-interfering, stateless
280-
* predicate to apply to each element to determine if it
281-
* should be included
282-
*
334+
* predicate to apply to each element to determine if it
335+
* should be included
283336
* @return the filtered list
284337
*/
285338
default ImmutableList<T> filter(Predicate<? super T> predicate) {
@@ -290,7 +343,7 @@ default ImmutableList<T> filter(Predicate<? super T> predicate) {
290343
* Returns an immutable list consisting of the results of applying the given
291344
* function to the elements of this list.
292345
*
293-
* @param <R> The element type of the result list
346+
* @param <R> The element type of the result list
294347
* @param function a non-interfering, stateless function to apply to each element
295348
* @return the mapped list of the same length as this
296349
*/
@@ -339,9 +392,9 @@ default ImmutableList<T> stripPrefix(ImmutableList<? extends T> prefix) {
339392
*
340393
* @return last element of this list
341394
*/
342-
default T last() {
395+
default T last() throws NoSuchElementException {
343396
if (isEmpty()) {
344-
throw new IllegalStateException("last() called on empty list");
397+
throw new NoSuchElementException("last() called on empty list");
345398
}
346399
ImmutableList<T> remainder = this;
347400
while (!remainder.tail().isEmpty()) {
@@ -358,10 +411,23 @@ default T last() {
358411
* @param idx the 0-based index of the element
359412
* @return the element at index idx.
360413
* @throws IndexOutOfBoundsException if idx is less than 0 or at
361-
* least {@link #size()}.
414+
* least {@link #size()}.
362415
*/
363-
default T get(int idx) {
364-
return take(idx).head();
416+
T get(int idx);
417+
418+
/// compares two {@link ImmutableList} content-wise
419+
static boolean equals(
420+
ImmutableList<?> seq1, ImmutableList<?> seq2) {
421+
422+
final var size = seq1.size();
423+
if (size == seq2.size()) {
424+
for (int i = 0; i < size; i++) {
425+
if (!Objects.equals(seq1.get(i), seq2.get(i))) {
426+
return false;
427+
}
428+
}
429+
return true;
430+
}
431+
return false;
365432
}
366-
367433
}

0 commit comments

Comments
 (0)