[Collections] Add ImCollection.map

Change-Id: Ife0042c1eff978f0845c5114788dac2ba8b5b55a
diff --git a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/AbstractImList.java b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/AbstractImList.java
index 3a4e9eb..48ebc91 100644
--- a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/AbstractImList.java
+++ b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/AbstractImList.java
@@ -98,6 +98,9 @@
 	}
 	
 	
+	public abstract int size();
+	
+	
 	public abstract E get(final int index);
 	
 	public abstract int indexOf(final @Nullable Object o);
@@ -109,7 +112,7 @@
 	
 	public abstract void copyTo(int srcPos, Object[] dest, int destPos, int length);
 	
-	public abstract ImList<E> toImList();
-	public abstract ImIdentityList<E> toImIdentityList();
+	public abstract ImList<E> toList();
+	public abstract ImIdentityList<E> toIdentityList();
 	
 }
diff --git a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArrayIdentityList.java b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArrayIdentityList.java
index e3de1d2..534854a 100644
--- a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArrayIdentityList.java
+++ b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArrayIdentityList.java
@@ -20,9 +20,11 @@
 import java.util.List;
 import java.util.ListIterator;
 import java.util.NoSuchElementException;
+import java.util.Objects;
 import java.util.RandomAccess;
 import java.util.Spliterator;
 import java.util.Spliterators;
+import java.util.function.Function;
 
 import org.eclipse.statet.jcommons.collections.IdentityList;
 import org.eclipse.statet.jcommons.collections.ImIdentityList;
@@ -205,6 +207,9 @@
 		else if (l == 1) {
 			return new ImSingletonIdentityList<>(this.array[fromIndex]);
 		}
+		else if (fromIndex == 0) {
+			return new ImArrayIdentitySub0List<>(this.array, toIndex);
+		}
 		else {
 			return new ImArrayIdentitySubList<>(this.array, fromIndex, toIndex);
 		}
@@ -242,15 +247,25 @@
 	}
 	
 	@Override
-	public ImList<E> toImList() {
+	public ImList<E> toList() {
 		return new ImArrayList<>(this.array);
 	}
 	
 	@Override
-	public ImIdentityList<E> toImIdentityList() {
+	public ImIdentityList<E> toIdentityList() {
 		return this;
 	}
 	
+	@Override
+	@SuppressWarnings("unchecked")
+	public <R> MappingResult<R> map(final Function<E, R> mapper) {
+		final R[] result= (R[])new Object[this.array.length];
+		for (int i= 0; i < this.array.length; i++) {
+			result[i]= mapper.apply(this.array[i]);
+		}
+		return new ImArrayList<>(result);
+	}
+	
 	
 	@Override
 	public int hashCode() {
@@ -267,7 +282,7 @@
 			return true;
 		}
 		if (obj instanceof IdentityList) {
-			final List<?> other= (List<?>) obj;
+			final var other= (IdentityList<?>)obj;
 			if (this.array.length != other.size()) {
 				return false;
 			}
@@ -279,253 +294,14 @@
 			}
 			return true;
 		}
-		return false;
-	}
-	
-	@Override
-	public String toString() {
-		return Arrays.toString(this.array);
-	}
-	
-}
-
-@NonNullByDefault
-final class ImArrayIdentitySubList<E> extends AbstractImList<E> implements ImIdentityList<E>,
-		RandomAccess {
-	
-	
-	private class Iter extends AbstractImListIter<E> {
-		
-		
-		private int cursor;
-		
-		
-		Iter(final int index) {
-			this.cursor= index;
-		}
-		
-		
-		@Override
-		public boolean hasNext() {
-			return (this.cursor < ImArrayIdentitySubList.this.size);
-		}
-		
-		@Override
-		public int nextIndex() {
-			return this.cursor;
-		}
-		
-		@Override
-		public E next() {
-			if (this.cursor >= ImArrayIdentitySubList.this.size) {
-				throw new NoSuchElementException();
-			}
-			return ImArrayIdentitySubList.this.array[ImArrayIdentitySubList.this.offset + (this.cursor++)];
-		}
-		
-		@Override
-		public boolean hasPrevious() {
-			return (this.cursor > 0);
-		}
-		
-		@Override
-		public int previousIndex() {
-			return this.cursor - 1;
-		}
-		
-		@Override
-		public E previous() {
-			if (this.cursor <= 0 || ImArrayIdentitySubList.this.size <= 0) {
-				throw new NoSuchElementException();
-			}
-			return ImArrayIdentitySubList.this.array[ImArrayIdentitySubList.this.offset + (--this.cursor)];
-		}
-		
-	}
-	
-	
-	private final E[] array;
-	private final int offset;
-	private final int size;
-	
-	
-	public ImArrayIdentitySubList(final E[] array, final int fromIndex, final int toIndex) {
-		this.array= array;
-		this.offset= fromIndex;
-		this.size= toIndex - fromIndex;
-	}
-	
-	
-	@Override
-	public int size() {
-		return this.size;
-	}
-	
-	@Override
-	public boolean isEmpty() {
-		return false;
-	}
-	
-	@Override
-	public boolean contains(final @Nullable Object o) {
-		return (indexOf(o) >= 0);
-	}
-	
-	@Override
-	public boolean containsAll(final Collection<?> c) {
-		final Iterator<?> iter= c.iterator();
-		while(iter.hasNext()) {
-			if (indexOf(iter.next()) < 0) {
+		if (obj instanceof List) {
+			final var other= (List<?>)obj;
+			if (this.array.length != other.size()) {
 				return false;
 			}
-		}
-		return true;
-	}
-	
-	@Override
-	public E get(final int index) {
-		if (index < 0 || index >= this.size) {
-			throw new IndexOutOfBoundsException("index= " + index); //$NON-NLS-1$
-		}
-		return this.array[this.offset + index];
-	}
-	
-	@Override
-	public int indexOf(final @Nullable Object o) {
-		final int toIndex= this.offset + this.size;
-		for (int i= this.offset; i < toIndex; i++) {
-			if (o == this.array[i]) {
-				return i - this.offset;
-			}
-		}
-		return -1;
-	}
-	
-	@Override
-	public int lastIndexOf(final @Nullable Object o) {
-		for (int i= this.offset + this.size - 1; i >= this.offset; i--) {
-			if (o == this.array[i]) {
-				return i - this.offset;
-			}
-		}
-		return -1;
-	}
-	
-	
-	@Override
-	public Iterator<E> iterator() {
-		return new Iter(0);
-	}
-	
-	@Override
-	public ListIterator<E> listIterator() {
-		return new Iter(0);
-	}
-	
-	@Override
-	public ListIterator<E> listIterator(final int index) {
-		if (index < 0 || index > this.size) {
-			throw new IndexOutOfBoundsException("index= " + index); //$NON-NLS-1$
-		}
-		return new Iter(index);
-	}
-	
-	@Override
-	public Spliterator<E> spliterator() {
-		return Spliterators.spliterator(this.array, this.offset, this.offset + this.size,
-				Spliterator.IMMUTABLE | Spliterator.ORDERED );
-	}
-	
-	
-	@Override
-	@SuppressWarnings("unchecked")
-	public ImIdentityList<E> subList(final int fromIndex, final int toIndex) {
-		if (fromIndex < 0 || toIndex > this.size) {
-			throw new IndexOutOfBoundsException("fromIndex= " + fromIndex + ", toIndex= " + toIndex + ", size= " + this.size); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
-		}
-		if (fromIndex > toIndex) {
-			throw new IllegalArgumentException("fromIndex > toIndex: fromIndex= " + fromIndex + ", toIndex= " + toIndex); //$NON-NLS-1$ //$NON-NLS-2$
-		}
-		final int l= toIndex - fromIndex;
-		if (l == this.size) {
-			return this;
-		}
-		else if (l == 0) {
-			return ImEmptyIdentityList.INSTANCE;
-		}
-		else if (l == 1) {
-			return new ImSingletonIdentityList<>(this.array[this.offset + fromIndex]);
-		}
-		else {
-			return new ImArrayIdentitySubList<>(this.array, this.offset + fromIndex, this.offset + toIndex);
-		}
-	}
-	
-	@Override
-	public Object[] toArray() {
-		final Object[] dest= new Object[this.size];
-		System.arraycopy(this.array, this.offset, dest, 0, this.size);
-		return dest;
-	}
-	
-	@Override
-	@SuppressWarnings({ "unchecked", "null" })
-	public <T> T[] toArray(final T[] dest) {
-		if (dest.length < this.size) {
-			return Arrays.copyOfRange(this.array, this.offset, this.offset + this.size, (Class<? extends T[]>)dest.getClass());
-		}
-		System.arraycopy(this.array, this.offset, dest, 0, this.size);
-		if (dest.length > this.size) {
-			dest[this.size]= null;
-		}
-		return dest;
-	}
-	
-	@Override
-	public void copyTo(final Object[] dest, final int destPos) {
-		System.arraycopy(this.array, this.offset, dest, destPos, this.size);
-	}
-	
-	@Override
-	public void copyTo(final int srcPos, final Object[] dest, final int destPos, final int length) {
-		System.arraycopy(this.array, this.offset + srcPos, dest, destPos, length);
-	}
-	
-	@Override
-	public ImList<E> toImList() {
-		return new ImArraySubList<>(this.array, this.offset, this.offset + this.size);
-	}
-	
-	@Override
-	public ImIdentityList<E> toImIdentityList() {
-		return this;
-	}
-	
-	
-	@Override
-	public int hashCode() {
-		int hashCode= 1;
-		final int toIndex= this.offset + this.size;
-		for (int i= this.offset; i < toIndex; i++) {
-			hashCode= 31 * hashCode + ((this.array[i] != null) ? this.array[i].hashCode() : 0);
-		}
-		return hashCode;
-	}
-	
-	@Override
-	public boolean equals(final @Nullable Object obj) {
-		if (obj == this) {
-			return true;
-		}
-		if (obj instanceof IdentityList) {
-			final List<?> other= (List<?>) obj;
-			if (this.size != other.size()) {
-				return false;
-			}
-			final ListIterator<?> otherIter= other.listIterator();
-			final int toIndex= this.offset + this.size;
-			for (int i= this.offset; i < toIndex; i++) {
-				if (this.array[i] != otherIter.next()) {
+			final Iterator<?> otherIter= other.iterator();
+			for (int i= 0; i < this.array.length; i++) {
+				if (!Objects.equals(this.array[i], otherIter.next())) {
 					return false;
 				}
 			}
@@ -536,7 +312,7 @@
 	
 	@Override
 	public String toString() {
-		return Arrays.toString(toArray());
+		return Arrays.toString(this.array);
 	}
 	
 }
diff --git a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArrayIdentitySet.java b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArrayIdentitySet.java
index 33cd479..3c77db7 100644
--- a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArrayIdentitySet.java
+++ b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArrayIdentitySet.java
@@ -22,7 +22,9 @@
 import java.util.Set;
 import java.util.Spliterator;
 import java.util.Spliterators;
+import java.util.function.Function;
 
+import org.eclipse.statet.jcommons.collections.IdentitySet;
 import org.eclipse.statet.jcommons.collections.ImIdentityList;
 import org.eclipse.statet.jcommons.collections.ImIdentitySet;
 import org.eclipse.statet.jcommons.collections.ImList;
@@ -123,6 +125,10 @@
 		return (indexOf(o) >= 0);
 	}
 	
+	public boolean containsEquals(final @Nullable Object o) {
+		return (indexOfEquals(o) >= 0);
+	}
+	
 	@Override
 	public boolean containsAll(final Collection<?> c) {
 		final Iterator<?> e= c.iterator();
@@ -134,6 +140,16 @@
 		return true;
 	}
 	
+	public boolean containsAllEquals(final Collection<?> c) {
+		final Iterator<?> e= c.iterator();
+		while (e.hasNext()) {
+			if (indexOfEquals(e.next()) < 0) {
+				return false;
+			}
+		}
+		return true;
+	}
+	
 	@Override
 	public E get(final int index) {
 		return this.array[index];
@@ -159,6 +175,25 @@
 		return -1;
 	}
 	
+	public int indexOfEquals(final @Nullable Object o) {
+		if (o == null) {
+			for (int i= 0; i < this.array.length; i++) {
+				if (null == this.array[i]) {
+					return i;
+				}
+			}
+			return -1;
+		}
+		else {
+			for (int i= 0; i < this.array.length; i++) {
+				if (o.equals(this.array[i])) {
+					return i;
+				}
+			}
+			return -1;
+		}
+	}
+	
 	
 	@Override
 	public Iterator<E> iterator() {
@@ -204,15 +239,25 @@
 	}
 	
 	@Override
-	public ImList<E> toImList() {
+	public ImList<E> toList() {
 		return new ImArrayList<>(this.array);
 	}
 	
 	@Override
-	public ImIdentityList<E> toImIdentityList() {
+	public ImIdentityList<E> toIdentityList() {
 		return new ImArrayIdentityList<>(this.array);
 	}
 	
+	@Override
+	@SuppressWarnings("unchecked")
+	public <R> MappingResult<R> map(final Function<E, R> mapper) {
+		final R[] result= (R[])new Object[this.array.length];
+		for (int i= 0; i < this.array.length; i++) {
+			result[i]= mapper.apply(this.array[i]);
+		}
+		return new ImArrayList<>(result);
+	}
+	
 	
 	@Override
 	public int hashCode() {
@@ -230,11 +275,16 @@
 		if (obj == this) {
 			return true;
 		}
-		if (obj instanceof Set) {
-			final Set<?> other= (Set<?>) obj;
+		if (obj instanceof IdentitySet) {
+			final var other= (IdentitySet<?>)obj;
 			return (this.array.length == other.size()
 					&& containsAll(other) );
 		}
+		if (obj instanceof Set) {
+			final var other= (Set<?>)obj;
+			return (this.array.length == other.size()
+					&& containsAllEquals(other) );
+		}
 		return false;
 	}
 	
diff --git a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArrayIdentitySub0List.java b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArrayIdentitySub0List.java
new file mode 100644
index 0000000..685427a
--- /dev/null
+++ b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArrayIdentitySub0List.java
@@ -0,0 +1,307 @@
+/*=============================================================================#
+ # Copyright (c) 2009, 2021 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.jcommons.collections;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.RandomAccess;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.function.Function;
+
+import org.eclipse.statet.jcommons.collections.IdentityList;
+import org.eclipse.statet.jcommons.collections.ImIdentityList;
+import org.eclipse.statet.jcommons.collections.ImList;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+
+@NonNullByDefault
+public final class ImArrayIdentitySub0List<E> extends AbstractImList<E> implements ImIdentityList<E>,
+		RandomAccess {
+	
+	
+	private class Iter extends AbstractImListIter<E> {
+		
+		
+		private int cursor;
+		
+		
+		Iter(final int index) {
+			this.cursor= index;
+		}
+		
+		
+		@Override
+		public boolean hasNext() {
+			return (this.cursor < ImArrayIdentitySub0List.this.size);
+		}
+		
+		@Override
+		public int nextIndex() {
+			return this.cursor;
+		}
+		
+		@Override
+		public E next() {
+			if (this.cursor >= ImArrayIdentitySub0List.this.size) {
+				throw new NoSuchElementException();
+			}
+			return ImArrayIdentitySub0List.this.array[this.cursor++];
+		}
+		
+		@Override
+		public boolean hasPrevious() {
+			return (this.cursor > 0);
+		}
+		
+		@Override
+		public int previousIndex() {
+			return this.cursor - 1;
+		}
+		
+		@Override
+		public E previous() {
+			if (this.cursor <= 0 || ImArrayIdentitySub0List.this.size <= 0) {
+				throw new NoSuchElementException();
+			}
+			return ImArrayIdentitySub0List.this.array[--this.cursor];
+		}
+		
+	}
+	
+	
+	private final E[] array;
+	private final int size;
+	
+	
+	public ImArrayIdentitySub0List(final E[] array, final int toIndex) {
+		this.array= array;
+		this.size= toIndex;
+	}
+	
+	
+	@Override
+	public int size() {
+		return this.size;
+	}
+	
+	@Override
+	public boolean isEmpty() {
+		return false;
+	}
+	
+	@Override
+	public boolean contains(final @Nullable Object o) {
+		return (indexOf(o) >= 0);
+	}
+	
+	@Override
+	public boolean containsAll(final Collection<?> c) {
+		final Iterator<?> iter= c.iterator();
+		while(iter.hasNext()) {
+			if (indexOf(iter.next()) < 0) {
+				return false;
+			}
+		}
+		return true;
+	}
+	
+	@Override
+	public E get(final int index) {
+		if (index < 0 || index >= this.size) {
+			throw new IndexOutOfBoundsException("index= " + index); //$NON-NLS-1$
+		}
+		return this.array[index];
+	}
+	
+	@Override
+	public int indexOf(final @Nullable Object o) {
+		for (int i= 0; i < this.size; i++) {
+			if (o == this.array[i]) {
+				return i;
+			}
+		}
+		return -1;
+	}
+	
+	@Override
+	public int lastIndexOf(final @Nullable Object o) {
+		for (int i= this.size - 1; i >= 0; i--) {
+			if (o == this.array[i]) {
+				return i;
+			}
+		}
+		return -1;
+	}
+	
+	
+	@Override
+	public Iterator<E> iterator() {
+		return new Iter(0);
+	}
+	
+	@Override
+	public ListIterator<E> listIterator() {
+		return new Iter(0);
+	}
+	
+	@Override
+	public ListIterator<E> listIterator(final int index) {
+		if (index < 0 || index > this.size) {
+			throw new IndexOutOfBoundsException("index= " + index); //$NON-NLS-1$
+		}
+		return new Iter(index);
+	}
+	
+	@Override
+	public Spliterator<E> spliterator() {
+		return Spliterators.spliterator(this.array, 0, this.size,
+				Spliterator.IMMUTABLE | Spliterator.ORDERED );
+	}
+	
+	
+	@Override
+	@SuppressWarnings("unchecked")
+	public ImIdentityList<E> subList(final int fromIndex, final int toIndex) {
+		if (fromIndex < 0 || toIndex > this.size) {
+			throw new IndexOutOfBoundsException("fromIndex= " + fromIndex + ", toIndex= " + toIndex + ", size= " + this.size); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		}
+		if (fromIndex > toIndex) {
+			throw new IllegalArgumentException("fromIndex > toIndex: fromIndex= " + fromIndex + ", toIndex= " + toIndex); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+		final int l= toIndex - fromIndex;
+		if (l == this.size) {
+			return this;
+		}
+		else if (l == 0) {
+			return ImEmptyIdentityList.INSTANCE;
+		}
+		else if (l == 1) {
+			return new ImSingletonIdentityList<>(this.array[fromIndex]);
+		}
+		else if (fromIndex == 0) {
+			return new ImArrayIdentitySub0List<>(this.array, toIndex);
+		}
+		else {
+			return new ImArrayIdentitySubList<>(this.array, fromIndex, toIndex);
+		}
+	}
+	
+	@Override
+	public Object[] toArray() {
+		final Object[] dest= new Object[this.size];
+		System.arraycopy(this.array, 0, dest, 0, this.size);
+		return dest;
+	}
+	
+	@Override
+	@SuppressWarnings({ "unchecked", "null" })
+	public <T> T[] toArray(final T[] dest) {
+		if (dest.length < this.size) {
+			return Arrays.copyOf(this.array, this.size, (Class<? extends T[]>)dest.getClass());
+		}
+		System.arraycopy(this.array, 0, dest, 0, this.size);
+		if (dest.length > this.size) {
+			dest[this.size]= null;
+		}
+		return dest;
+	}
+	
+	@Override
+	public void copyTo(final Object[] dest, final int destPos) {
+		System.arraycopy(this.array, 0, dest, destPos, this.size);
+	}
+	
+	@Override
+	public void copyTo(final int srcPos, final Object[] dest, final int destPos, final int length) {
+		System.arraycopy(this.array, 0 + srcPos, dest, destPos, length);
+	}
+	
+	@Override
+	public ImList<E> toList() {
+		return new ImArraySub0List<>(this.array, this.size);
+	}
+	
+	@Override
+	public ImIdentityList<E> toIdentityList() {
+		return this;
+	}
+	
+	@Override
+	@SuppressWarnings("unchecked")
+	public <R> MappingResult<R> map(final Function<E, R> mapper) {
+		final R[] result= (R[])new Object[this.size];
+		for (int i= 0; i < this.size; i++) {
+			result[i]= mapper.apply(this.array[i]);
+		}
+		return new ImArrayList<>(result);
+	}
+	
+	
+	@Override
+	public int hashCode() {
+		int hashCode= 1;
+		for (int i= 0; i < this.size; i++) {
+			hashCode= 31 * hashCode + ((this.array[i] != null) ? this.array[i].hashCode() : 0);
+		}
+		return hashCode;
+	}
+	
+	@Override
+	public boolean equals(final @Nullable Object obj) {
+		if (obj == this) {
+			return true;
+		}
+		if (obj instanceof IdentityList) {
+			final var other= (IdentityList<?>)obj;
+			if (this.size != other.size()) {
+				return false;
+			}
+			final ListIterator<?> otherIter= other.listIterator();
+			for (int i= 0; i < this.size; i++) {
+				if (this.array[i] != otherIter.next()) {
+					return false;
+				}
+			}
+			return true;
+		}
+		if (obj instanceof List) {
+			final var other= (List<?>)obj;
+			if (this.size != other.size()) {
+				return false;
+			}
+			final ListIterator<?> otherIter= other.listIterator();
+			for (int i= 0; i < this.size; i++) {
+				if (!Objects.equals(this.array[i], otherIter.next())) {
+					return false;
+				}
+			}
+			return true;
+		}
+		return false;
+	}
+	
+	@Override
+	public String toString() {
+		return Arrays.toString(toArray());
+	}
+	
+}
diff --git a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArrayIdentitySubList.java b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArrayIdentitySubList.java
new file mode 100644
index 0000000..76bac7a
--- /dev/null
+++ b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArrayIdentitySubList.java
@@ -0,0 +1,317 @@
+/*=============================================================================#
+ # Copyright (c) 2009, 2021 Stephan Wahlbrink and others.
+ # 
+ # This program and the accompanying materials are made available under the
+ # terms of the Eclipse Public License 2.0 which is available at
+ # https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
+ # which is available at https://www.apache.org/licenses/LICENSE-2.0.
+ # 
+ # SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
+ # 
+ # Contributors:
+ #     Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
+ #=============================================================================*/
+
+package org.eclipse.statet.internal.jcommons.collections;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
+import java.util.Objects;
+import java.util.RandomAccess;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.function.Function;
+
+import org.eclipse.statet.jcommons.collections.IdentityList;
+import org.eclipse.statet.jcommons.collections.ImIdentityList;
+import org.eclipse.statet.jcommons.collections.ImList;
+import org.eclipse.statet.jcommons.lang.NonNullByDefault;
+import org.eclipse.statet.jcommons.lang.Nullable;
+
+
+/**
+ * Constant list implementation based on an array.
+ * <p>
+ * Comparable to <code>Collections.unmodifiableList(Array.asList(...))</code>.</p>
+ * 
+ * @since de.walware.ecommons.coremisc 1.5
+ */
+@NonNullByDefault
+public final class ImArrayIdentitySubList<E> extends AbstractImList<E> implements ImIdentityList<E>,
+		RandomAccess {
+	
+	
+	private class Iter extends AbstractImListIter<E> {
+		
+		
+		private int cursor;
+		
+		
+		Iter(final int index) {
+			this.cursor= index;
+		}
+		
+		
+		@Override
+		public boolean hasNext() {
+			return (this.cursor < ImArrayIdentitySubList.this.size);
+		}
+		
+		@Override
+		public int nextIndex() {
+			return this.cursor;
+		}
+		
+		@Override
+		public E next() {
+			if (this.cursor >= ImArrayIdentitySubList.this.size) {
+				throw new NoSuchElementException();
+			}
+			return ImArrayIdentitySubList.this.array[ImArrayIdentitySubList.this.offset + (this.cursor++)];
+		}
+		
+		@Override
+		public boolean hasPrevious() {
+			return (this.cursor > 0);
+		}
+		
+		@Override
+		public int previousIndex() {
+			return this.cursor - 1;
+		}
+		
+		@Override
+		public E previous() {
+			if (this.cursor <= 0 || ImArrayIdentitySubList.this.size <= 0) {
+				throw new NoSuchElementException();
+			}
+			return ImArrayIdentitySubList.this.array[ImArrayIdentitySubList.this.offset + (--this.cursor)];
+		}
+		
+	}
+	
+	
+	private final E[] array;
+	private final int offset;
+	private final int size;
+	
+	
+	public ImArrayIdentitySubList(final E[] array, final int fromIndex, final int toIndex) {
+		this.array= array;
+		this.offset= fromIndex;
+		this.size= toIndex - fromIndex;
+	}
+	
+	
+	@Override
+	public int size() {
+		return this.size;
+	}
+	
+	@Override
+	public boolean isEmpty() {
+		return false;
+	}
+	
+	@Override
+	public boolean contains(final @Nullable Object o) {
+		return (indexOf(o) >= 0);
+	}
+	
+	@Override
+	public boolean containsAll(final Collection<?> c) {
+		final Iterator<?> iter= c.iterator();
+		while(iter.hasNext()) {
+			if (indexOf(iter.next()) < 0) {
+				return false;
+			}
+		}
+		return true;
+	}
+	
+	@Override
+	public E get(final int index) {
+		if (index < 0 || index >= this.size) {
+			throw new IndexOutOfBoundsException("index= " + index); //$NON-NLS-1$
+		}
+		return this.array[this.offset + index];
+	}
+	
+	@Override
+	public int indexOf(final @Nullable Object o) {
+		final int toIndex= this.offset + this.size;
+		for (int i= this.offset; i < toIndex; i++) {
+			if (o == this.array[i]) {
+				return i - this.offset;
+			}
+		}
+		return -1;
+	}
+	
+	@Override
+	public int lastIndexOf(final @Nullable Object o) {
+		for (int i= this.offset + this.size - 1; i >= this.offset; i--) {
+			if (o == this.array[i]) {
+				return i - this.offset;
+			}
+		}
+		return -1;
+	}
+	
+	
+	@Override
+	public Iterator<E> iterator() {
+		return new Iter(0);
+	}
+	
+	@Override
+	public ListIterator<E> listIterator() {
+		return new Iter(0);
+	}
+	
+	@Override
+	public ListIterator<E> listIterator(final int index) {
+		if (index < 0 || index > this.size) {
+			throw new IndexOutOfBoundsException("index= " + index); //$NON-NLS-1$
+		}
+		return new Iter(index);
+	}
+	
+	@Override
+	public Spliterator<E> spliterator() {
+		return Spliterators.spliterator(this.array, this.offset, this.offset + this.size,
+				Spliterator.IMMUTABLE | Spliterator.ORDERED );
+	}
+	
+	
+	@Override
+	@SuppressWarnings("unchecked")
+	public ImIdentityList<E> subList(final int fromIndex, final int toIndex) {
+		if (fromIndex < 0 || toIndex > this.size) {
+			throw new IndexOutOfBoundsException("fromIndex= " + fromIndex + ", toIndex= " + toIndex + ", size= " + this.size); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+		}
+		if (fromIndex > toIndex) {
+			throw new IllegalArgumentException("fromIndex > toIndex: fromIndex= " + fromIndex + ", toIndex= " + toIndex); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+		final int l= toIndex - fromIndex;
+		if (l == this.size) {
+			return this;
+		}
+		else if (l == 0) {
+			return ImEmptyIdentityList.INSTANCE;
+		}
+		else if (l == 1) {
+			return new ImSingletonIdentityList<>(this.array[this.offset + fromIndex]);
+		}
+		else {
+			return new ImArrayIdentitySubList<>(this.array, this.offset + fromIndex, this.offset + toIndex);
+		}
+	}
+	
+	@Override
+	public Object[] toArray() {
+		final Object[] dest= new Object[this.size];
+		System.arraycopy(this.array, this.offset, dest, 0, this.size);
+		return dest;
+	}
+	
+	@Override
+	@SuppressWarnings({ "unchecked", "null" })
+	public <T> T[] toArray(final T[] dest) {
+		if (dest.length < this.size) {
+			return Arrays.copyOfRange(this.array, this.offset, this.offset + this.size, (Class<? extends T[]>)dest.getClass());
+		}
+		System.arraycopy(this.array, this.offset, dest, 0, this.size);
+		if (dest.length > this.size) {
+			dest[this.size]= null;
+		}
+		return dest;
+	}
+	
+	@Override
+	public void copyTo(final Object[] dest, final int destPos) {
+		System.arraycopy(this.array, this.offset, dest, destPos, this.size);
+	}
+	
+	@Override
+	public void copyTo(final int srcPos, final Object[] dest, final int destPos, final int length) {
+		System.arraycopy(this.array, this.offset + srcPos, dest, destPos, length);
+	}
+	
+	@Override
+	public ImList<E> toList() {
+		return new ImArraySubList<>(this.array, this.offset, this.offset + this.size);
+	}
+	
+	@Override
+	public ImIdentityList<E> toIdentityList() {
+		return this;
+	}
+	
+	@Override
+	@SuppressWarnings("unchecked")
+	public <R> MappingResult<R> map(final Function<E, R> mapper) {
+		final R[] result= (R[])new Object[this.size];
+		for (int i= 0; i < this.size; i++) {
+			result[i]= mapper.apply(this.array[this.offset + i]);
+		}
+		return new ImArrayList<>(result);
+	}
+	
+	
+	@Override
+	public int hashCode() {
+		int hashCode= 1;
+		final int toIndex= this.offset + this.size;
+		for (int i= this.offset; i < toIndex; i++) {
+			hashCode= 31 * hashCode + ((this.array[i] != null) ? this.array[i].hashCode() : 0);
+		}
+		return hashCode;
+	}
+	
+	@Override
+	public boolean equals(final @Nullable Object obj) {
+		if (obj == this) {
+			return true;
+		}
+		if (obj instanceof IdentityList) {
+			final var other= (IdentityList<?>)obj;
+			if (this.size != other.size()) {
+				return false;
+			}
+			final ListIterator<?> otherIter= other.listIterator();
+			final int toIndex= this.offset + this.size;
+			for (int i= this.offset; i < toIndex; i++) {
+				if (this.array[i] != otherIter.next()) {
+					return false;
+				}
+			}
+			return true;
+		}
+		if (obj instanceof List) {
+			final var other= (List<?>)obj;
+			if (this.size != other.size()) {
+				return false;
+			}
+			final ListIterator<?> otherIter= other.listIterator();
+			final int toIndex= this.offset + this.size;
+			for (int i= this.offset; i < toIndex; i++) {
+				if (!Objects.equals(this.array[i], otherIter.next())) {
+					return false;
+				}
+			}
+			return true;
+		}
+		return false;
+	}
+	
+	@Override
+	public String toString() {
+		return Arrays.toString(toArray());
+	}
+	
+}
diff --git a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArrayList.java b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArrayList.java
index 90a10ff..0cc35e1 100644
--- a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArrayList.java
+++ b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArrayList.java
@@ -24,7 +24,9 @@
 import java.util.RandomAccess;
 import java.util.Spliterator;
 import java.util.Spliterators;
+import java.util.function.Function;
 
+import org.eclipse.statet.jcommons.collections.ImCollection.MappingResult;
 import org.eclipse.statet.jcommons.collections.ImIdentityList;
 import org.eclipse.statet.jcommons.collections.ImList;
 import org.eclipse.statet.jcommons.lang.NonNullByDefault;
@@ -40,7 +42,7 @@
  */
 @NonNullByDefault
 public final class ImArrayList<E> extends AbstractImList<E> implements ImList<E>,
-		RandomAccess {
+		RandomAccess, MappingResult<E> {
 	
 	
 	private class Iter extends AbstractImListIter<E> {
@@ -265,15 +267,25 @@
 	}
 	
 	@Override
-	public ImList<E> toImList() {
+	public ImList<E> toList() {
 		return this;
 	}
 	
 	@Override
-	public ImIdentityList<E> toImIdentityList() {
+	public ImIdentityList<E> toIdentityList() {
 		return new ImArrayIdentityList<>(this.array);
 	}
 	
+	@Override
+	@SuppressWarnings("unchecked")
+	public <R> MappingResult<R> map(final Function<E, R> mapper) {
+		final R[] result= (R[])new Object[this.array.length];
+		for (int i= 0; i < this.array.length; i++) {
+			result[i]= mapper.apply(this.array[i]);
+		}
+		return new ImArrayList<>(result);
+	}
+	
 	
 	@Override
 	public int hashCode() {
@@ -290,7 +302,7 @@
 			return true;
 		}
 		if (obj instanceof List) {
-			final List<?> other= (List<?>) obj;
+			final var other= (List<?>)obj;
 			if (this.array.length != other.size()) {
 				return false;
 			}
diff --git a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArraySet.java b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArraySet.java
index 6b47cb4..adbe7e4 100644
--- a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArraySet.java
+++ b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArraySet.java
@@ -22,6 +22,7 @@
 import java.util.Set;
 import java.util.Spliterator;
 import java.util.Spliterators;
+import java.util.function.Function;
 
 import org.eclipse.statet.jcommons.collections.ImIdentityList;
 import org.eclipse.statet.jcommons.collections.ImList;
@@ -224,15 +225,25 @@
 	}
 	
 	@Override
-	public ImList<E> toImList() {
+	public ImList<E> toList() {
 		return new ImArrayList<>(this.array);
 	}
 	
 	@Override
-	public ImIdentityList<E> toImIdentityList() {
+	public ImIdentityList<E> toIdentityList() {
 		return new ImArrayIdentityList<>(this.array);
 	}
 	
+	@Override
+	@SuppressWarnings("unchecked")
+	public <R> MappingResult<R> map(final Function<E, R> mapper) {
+		final R[] result= (R[])new Object[this.array.length];
+		for (int i= 0; i < this.array.length; i++) {
+			result[i]= mapper.apply(this.array[i]);
+		}
+		return new ImArrayList<>(result);
+	}
+	
 	
 	@Override
 	public int hashCode() {
@@ -251,7 +262,7 @@
 			return true;
 		}
 		if (obj instanceof Set) {
-			final Set<?> other= (Set<?>) obj;
+			final var other= (Set<?>)obj;
 			return (this.array.length == other.size()
 					&& containsAll(other) );
 		}
diff --git a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArraySub0List.java b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArraySub0List.java
index c5d5d85..f0d8453 100644
--- a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArraySub0List.java
+++ b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArraySub0List.java
@@ -24,6 +24,7 @@
 import java.util.RandomAccess;
 import java.util.Spliterator;
 import java.util.Spliterators;
+import java.util.function.Function;
 
 import org.eclipse.statet.jcommons.collections.ImIdentityList;
 import org.eclipse.statet.jcommons.collections.ImList;
@@ -254,13 +255,23 @@
 	}
 	
 	@Override
-	public ImList<E> toImList() {
+	public ImList<E> toList() {
 		return this;
 	}
 	
 	@Override
-	public ImIdentityList<E> toImIdentityList() {
-		return new ImArrayIdentitySubList<>(this.array, 0, this.size);
+	public ImIdentityList<E> toIdentityList() {
+		return new ImArrayIdentitySub0List<>(this.array, this.size);
+	}
+	
+	@Override
+	@SuppressWarnings("unchecked")
+	public <R> MappingResult<R> map(final Function<E, R> mapper) {
+		final R[] result= (R[])new Object[this.size];
+		for (int i= 0; i < this.size; i++) {
+			result[i]= mapper.apply(this.array[i]);
+		}
+		return new ImArrayList<>(result);
 	}
 	
 	
@@ -279,7 +290,7 @@
 			return true;
 		}
 		if (obj instanceof List) {
-			final List<?> other= (List<?>) obj;
+			final var other= (List<?>)obj;
 			if (this.size != other.size()) {
 				return false;
 			}
diff --git a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArraySubList.java b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArraySubList.java
index da045b4..e5994ff 100644
--- a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArraySubList.java
+++ b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImArraySubList.java
@@ -24,6 +24,7 @@
 import java.util.RandomAccess;
 import java.util.Spliterator;
 import java.util.Spliterators;
+import java.util.function.Function;
 
 import org.eclipse.statet.jcommons.collections.ImIdentityList;
 import org.eclipse.statet.jcommons.collections.ImList;
@@ -255,15 +256,25 @@
 	}
 	
 	@Override
-	public ImList<E> toImList() {
+	public ImList<E> toList() {
 		return this;
 	}
 	
 	@Override
-	public ImIdentityList<E> toImIdentityList() {
+	public ImIdentityList<E> toIdentityList() {
 		return new ImArrayIdentitySubList<>(this.array, this.offset, this.offset + this.size);
 	}
 	
+	@Override
+	@SuppressWarnings("unchecked")
+	public <R> MappingResult<R> map(final Function<E, R> mapper) {
+		final R[] result= (R[])new Object[this.size];
+		for (int i= 0; i < this.size; i++) {
+			result[i]= mapper.apply(this.array[this.offset + i]);
+		}
+		return new ImArrayList<>(result);
+	}
+	
 	
 	@Override
 	public int hashCode() {
@@ -281,7 +292,7 @@
 			return true;
 		}
 		if (obj instanceof List) {
-			final List<?> other= (List<?>) obj;
+			final var other= (List<?>)obj;
 			if (this.size != other.size()) {
 				return false;
 			}
diff --git a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImEmptyIdentityList.java b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImEmptyIdentityList.java
index a5c2073..afd414c 100644
--- a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImEmptyIdentityList.java
+++ b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImEmptyIdentityList.java
@@ -22,8 +22,8 @@
 import java.util.RandomAccess;
 import java.util.Spliterator;
 import java.util.Spliterators;
+import java.util.function.Function;
 
-import org.eclipse.statet.jcommons.collections.IdentityList;
 import org.eclipse.statet.jcommons.collections.ImIdentityList;
 import org.eclipse.statet.jcommons.collections.ImList;
 import org.eclipse.statet.jcommons.lang.NonNullByDefault;
@@ -181,15 +181,24 @@
 	
 	@Override
 	@SuppressWarnings("unchecked")
-	public ImList<E> toImList() {
+	public ImList<E> toList() {
 		return ImEmptyList.INSTANCE;
 	}
 	
 	@Override
-	public ImIdentityList<E> toImIdentityList() {
+	public ImIdentityList<E> toIdentityList() {
 		return this;
 	}
 	
+	@Override
+	@SuppressWarnings({ "unchecked", "unused" })
+	public <R> MappingResult<R> map(final Function<E, R> mapper) {
+		if (mapper == null) {
+			throw new NullPointerException();
+		}
+		return ImEmptyList.INSTANCE;
+	}
+	
 	
 	@Override
 	public int hashCode() {
@@ -201,8 +210,8 @@
 		if (obj == this) {
 			return true;
 		}
-		if (obj instanceof IdentityList) {
-			final List<?> other= (List<?>) obj;
+		if (obj instanceof List) {
+			final var other= (List<?>)obj;
 			return (other.isEmpty());
 		}
 		return false;
diff --git a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImEmptyIdentitySet.java b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImEmptyIdentitySet.java
index 483bc70..6de13db 100644
--- a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImEmptyIdentitySet.java
+++ b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImEmptyIdentitySet.java
@@ -22,8 +22,8 @@
 import java.util.Set;
 import java.util.Spliterator;
 import java.util.Spliterators;
+import java.util.function.Function;
 
-import org.eclipse.statet.jcommons.collections.IdentitySet;
 import org.eclipse.statet.jcommons.collections.ImIdentityList;
 import org.eclipse.statet.jcommons.collections.ImIdentitySet;
 import org.eclipse.statet.jcommons.collections.ImList;
@@ -158,16 +158,25 @@
 	
 	@Override
 	@SuppressWarnings("unchecked")
-	public ImList<E> toImList() {
+	public ImList<E> toList() {
 		return ImEmptyList.INSTANCE;
 	}
 	
 	@Override
 	@SuppressWarnings("unchecked")
-	public ImIdentityList<E> toImIdentityList() {
+	public ImIdentityList<E> toIdentityList() {
 		return ImEmptyIdentityList.INSTANCE;
 	}
 	
+	@Override
+	@SuppressWarnings({ "unchecked", "unused" })
+	public <R> MappingResult<R> map(final Function<E, R> mapper) {
+		if (mapper == null) {
+			throw new NullPointerException();
+		}
+		return ImEmptyList.INSTANCE;
+	}
+	
 	
 	@Override
 	public int hashCode() {
@@ -179,8 +188,8 @@
 		if (obj == this) {
 			return true;
 		}
-		if (obj instanceof IdentitySet) {
-			final Set<?> other= (Set<?>) obj;
+		if (obj instanceof Set) {
+			final var other= (Set<?>)obj;
 			return (other.isEmpty());
 		}
 		return false;
diff --git a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImEmptyList.java b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImEmptyList.java
index 54ea75b..15b67ea 100644
--- a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImEmptyList.java
+++ b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImEmptyList.java
@@ -22,7 +22,9 @@
 import java.util.RandomAccess;
 import java.util.Spliterator;
 import java.util.Spliterators;
+import java.util.function.Function;
 
+import org.eclipse.statet.jcommons.collections.ImCollection.MappingResult;
 import org.eclipse.statet.jcommons.collections.ImIdentityList;
 import org.eclipse.statet.jcommons.collections.ImList;
 import org.eclipse.statet.jcommons.lang.NonNullByDefault;
@@ -38,7 +40,7 @@
  */
 @NonNullByDefault
 public final class ImEmptyList<E> extends AbstractImList<E> implements ImList<E>,
-		RandomAccess {
+		RandomAccess, MappingResult<E> {
 	
 	
 	@SuppressWarnings("rawtypes")
@@ -179,16 +181,25 @@
 	}
 	
 	@Override
-	public ImList<E> toImList() {
+	public ImList<E> toList() {
 		return this;
 	}
 	
 	@Override
 	@SuppressWarnings("unchecked")
-	public ImIdentityList<E> toImIdentityList() {
+	public ImIdentityList<E> toIdentityList() {
 		return ImEmptyIdentityList.INSTANCE;
 	}
 	
+	@Override
+	@SuppressWarnings({ "unchecked", "unused" })
+	public <R> MappingResult<R> map(final Function<E, R> mapper) {
+		if (mapper == null) {
+			throw new NullPointerException();
+		}
+		return INSTANCE;
+	}
+	
 	
 	@Override
 	public int hashCode() {
diff --git a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImEmptySet.java b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImEmptySet.java
index caecf8f..a1a6557 100644
--- a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImEmptySet.java
+++ b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImEmptySet.java
@@ -22,6 +22,7 @@
 import java.util.Set;
 import java.util.Spliterator;
 import java.util.Spliterators;
+import java.util.function.Function;
 
 import org.eclipse.statet.jcommons.collections.ImIdentityList;
 import org.eclipse.statet.jcommons.collections.ImList;
@@ -157,16 +158,25 @@
 	
 	@Override
 	@SuppressWarnings("unchecked")
-	public ImList<E> toImList() {
+	public ImList<E> toList() {
 		return ImEmptyList.INSTANCE;
 	}
 	
 	@Override
 	@SuppressWarnings("unchecked")
-	public ImIdentityList<E> toImIdentityList() {
+	public ImIdentityList<E> toIdentityList() {
 		return ImEmptyIdentityList.INSTANCE;
 	}
 	
+	@Override
+	@SuppressWarnings({ "unchecked", "unused" })
+	public <R> MappingResult<R> map(final Function<E, R> mapper) {
+		if (mapper == null) {
+			throw new NullPointerException();
+		}
+		return ImEmptyList.INSTANCE;
+	}
+	
 	
 	@Override
 	public int hashCode() {
diff --git a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImSingletonIdentityList.java b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImSingletonIdentityList.java
index a0c0d97..858c8dc 100644
--- a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImSingletonIdentityList.java
+++ b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImSingletonIdentityList.java
@@ -23,6 +23,7 @@
 import java.util.Objects;
 import java.util.RandomAccess;
 import java.util.Spliterator;
+import java.util.function.Function;
 
 import org.eclipse.statet.jcommons.collections.IdentityList;
 import org.eclipse.statet.jcommons.collections.ImIdentityList;
@@ -224,15 +225,21 @@
 	}
 	
 	@Override
-	public ImList<E> toImList() {
+	public ImList<E> toList() {
 		return new ImSingletonList<>(this.e);
 	}
 	
 	@Override
-	public ImIdentityList<E> toImIdentityList() {
+	public ImIdentityList<E> toIdentityList() {
 		return this;
 	}
 	
+	@Override
+	public <R> MappingResult<R> map(final Function<E, R> mapper) {
+		return new ImSingletonList<>(
+				mapper.apply(this.e) );
+	}
+	
 	
 	@Override
 	public int hashCode() {
@@ -247,9 +254,14 @@
 			return true;
 		}
 		if (obj instanceof IdentityList) {
-			final List<?> other= (List<?>) obj;
+			final var other= (IdentityList<?>)obj;
 			return (1 == other.size()
-					&& ((this.e == other.get(0))) );
+					&& (this.e == other.get(0)) );
+		}
+		if (obj instanceof List) {
+			final var other= (List<?>)obj;
+			return (1 == other.size()
+					&& Objects.equals(this.e, other.get(0)) );
 		}
 		return false;
 	}
diff --git a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImSingletonIdentitySet.java b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImSingletonIdentitySet.java
index e81b615..255dcb1 100644
--- a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImSingletonIdentitySet.java
+++ b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImSingletonIdentitySet.java
@@ -22,6 +22,7 @@
 import java.util.RandomAccess;
 import java.util.Set;
 import java.util.Spliterator;
+import java.util.function.Function;
 
 import org.eclipse.statet.jcommons.collections.IdentitySet;
 import org.eclipse.statet.jcommons.collections.ImIdentityList;
@@ -194,15 +195,21 @@
 	}
 	
 	@Override
-	public ImList<E> toImList() {
+	public ImList<E> toList() {
 		return new ImSingletonList<>(this.e);
 	}
 	
 	@Override
-	public ImIdentityList<E> toImIdentityList() {
+	public ImIdentityList<E> toIdentityList() {
 		return new ImSingletonIdentityList<>(this.e);
 	}
 	
+	@Override
+	public <R> MappingResult<R> map(final Function<E, R> mapper) {
+		return new ImSingletonList<>(
+				mapper.apply(this.e) );
+	}
+	
 	
 	@Override
 	public int hashCode() {
@@ -215,9 +222,14 @@
 			return true;
 		}
 		if (obj instanceof IdentitySet) {
-			final Set<?> other= (Set<?>) obj;
+			final var other= (IdentitySet<?>)obj;
 			return (1 == other.size()
-					&& contains(other.iterator().next()) );
+					&& (this.e == other.iterator().next()) );
+		}
+		if (obj instanceof Set) {
+			final var other= (Set<?>)obj;
+			return (1 == other.size()
+					&& Objects.equals(this.e, other.iterator().next()) );
 		}
 		return false;
 	}
diff --git a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImSingletonList.java b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImSingletonList.java
index b522157..2409e6f 100644
--- a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImSingletonList.java
+++ b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImSingletonList.java
@@ -23,7 +23,9 @@
 import java.util.Objects;
 import java.util.RandomAccess;
 import java.util.Spliterator;
+import java.util.function.Function;
 
+import org.eclipse.statet.jcommons.collections.ImCollection.MappingResult;
 import org.eclipse.statet.jcommons.collections.ImIdentityList;
 import org.eclipse.statet.jcommons.collections.ImList;
 import org.eclipse.statet.jcommons.lang.NonNullByDefault;
@@ -39,7 +41,7 @@
  */
 @NonNullByDefault
 public final class ImSingletonList<E> extends AbstractImList<E> implements ImList<E>,
-		RandomAccess {
+		RandomAccess, MappingResult<E> {
 	
 	
 	private class Iter extends AbstractImListIter<E> {
@@ -223,15 +225,21 @@
 	}
 	
 	@Override
-	public ImList<E> toImList() {
+	public ImList<E> toList() {
 		return this;
 	}
 	
 	@Override
-	public ImIdentityList<E> toImIdentityList() {
+	public ImIdentityList<E> toIdentityList() {
 		return new ImSingletonIdentityList<>(this.e);
 	}
 	
+	@Override
+	public <R> MappingResult<R> map(final Function<E, R> mapper) {
+		return new ImSingletonList<>(
+				mapper.apply(this.e) );
+	}
+	
 	
 	@Override
 	public int hashCode() {
@@ -246,7 +254,7 @@
 			return true;
 		}
 		if (obj instanceof List) {
-			final List<?> other= (List<?>) obj;
+			final var other= (List<?>)obj;
 			return (1 == other.size()
 					&& Objects.equals(this.e, other.get(0)) );
 		}
diff --git a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImSingletonSet.java b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImSingletonSet.java
index 2961042..9acd84c 100644
--- a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImSingletonSet.java
+++ b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/internal/jcommons/collections/ImSingletonSet.java
@@ -22,6 +22,7 @@
 import java.util.RandomAccess;
 import java.util.Set;
 import java.util.Spliterator;
+import java.util.function.Function;
 
 import org.eclipse.statet.jcommons.collections.ImIdentityList;
 import org.eclipse.statet.jcommons.collections.ImList;
@@ -193,15 +194,21 @@
 	}
 	
 	@Override
-	public ImList<E> toImList() {
+	public ImList<E> toList() {
 		return new ImSingletonList<>(this.e);
 	}
 	
 	@Override
-	public ImIdentityList<E> toImIdentityList() {
+	public ImIdentityList<E> toIdentityList() {
 		return new ImSingletonIdentityList<>(this.e);
 	}
 	
+	@Override
+	public <R> MappingResult<R> map(final Function<E, R> mapper) {
+		return new ImSingletonList<>(
+				mapper.apply(this.e) );
+	}
+	
 	
 	@Override
 	public int hashCode() {
@@ -214,7 +221,7 @@
 			return true;
 		}
 		if (obj instanceof Set) {
-			final Set<?> other= (Set<?>) obj;
+			final var other= (Set<?>)obj;
 			return (1 == other.size()
 					&& Objects.equals(this.e, other.iterator().next()) );
 		}
diff --git a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/jcommons/collections/ImCollection.java b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/jcommons/collections/ImCollection.java
index 1b5a25a..a289337 100644
--- a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/jcommons/collections/ImCollection.java
+++ b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/jcommons/collections/ImCollection.java
@@ -15,6 +15,7 @@
 package org.eclipse.statet.jcommons.collections;
 
 import java.util.Collection;
+import java.util.function.Function;
 
 import org.eclipse.statet.jcommons.lang.Immutable;
 import org.eclipse.statet.jcommons.lang.NonNullByDefault;
@@ -31,4 +32,16 @@
 public interface ImCollection<E> extends Collection<E>, Immutable {
 	
 	
+	interface MappingResult<R> {
+		
+		
+		ImList<R> toList();
+		
+		ImIdentityList<R> toIdentityList();
+		
+	}
+	
+	
+	<R> MappingResult<R> map(Function<E, R> mapper);
+	
 }
diff --git a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/jcommons/collections/ImCollections.java b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/jcommons/collections/ImCollections.java
index f7b387f..6d82337 100644
--- a/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/jcommons/collections/ImCollections.java
+++ b/jcommons/org.eclipse.statet.jcommons.util/src/org/eclipse/statet/jcommons/collections/ImCollections.java
@@ -215,7 +215,7 @@
 	@SuppressWarnings("unchecked")
 	public static <E> ImList<E> toList(final Collection<? extends E> c) {
 		if (c instanceof AbstractImList<?>) {
-			return ((AbstractImList<E>)c).toImList();
+			return ((AbstractImList<E>)c).toList();
 		}
 		final int n= c.size();
 		if (n == 0) {
@@ -734,7 +734,7 @@
 	@SuppressWarnings("unchecked")
 	public static <E> ImIdentityList<E> toIdentityList(final Collection<? extends E> c) {
 		if (c instanceof AbstractImList<?>) {
-			return ((AbstractImList<E>)c).toImIdentityList();
+			return ((AbstractImList<E>)c).toIdentityList();
 		}
 		final int n= c.size();
 		if (n == 0) {