33 * SPDX-License-Identifier: GPL-2.0-only */
44package org .key_project .util .collection ;
55
6+ import org .jspecify .annotations .Nullable ;
7+
8+ import java .lang .reflect .Array ;
69import java .util .*;
710import java .util .function .Function ;
811import java .util .function .Predicate ;
912import java .util .stream .Collector ;
13+ import java .util .stream .Collectors ;
1014import java .util .stream .Stream ;
1115import 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" )
1821public 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