in commons/src/main/java/org/apache/causeway/commons/collections/Can_Multiple.java [57:532]
record Can_Multiple<T>(List<T> elements) implements Can<T> {
// package private, but wrapped just in case
public List<T> elements() {
return Collections.unmodifiableList(elements);
}
@Override
public Optional<T> getFirst() {
return Optional.of(elements.get(0));
}
@Override
public Optional<T> getLast() {
return Optional.of(elements.get(size()-1));
}
@Override
public Cardinality getCardinality() {
return Cardinality.MULTIPLE;
}
@Override
public Stream<T> stream() {
return elements.stream();
}
@Override
public Stream<T> parallelStream() {
return elements.parallelStream();
}
@Override
public Optional<T> getSingleton() {
return Optional.empty();
}
@Override
public int size() {
return elements.size();
}
@Override
public boolean contains(final @Nullable T element) {
if(element==null) {
return false; // Can's dont't contain null
}
return elements.contains(element);
}
@Override
public Optional<T> get(final int elementIndex) {
// we do an index out of bounds check ourselves, in order to prevent any stack-traces,
// that pollute the heap
var size = size();
if(size==0) {
return Optional.empty();
}
var minIndex = 0;
var maxIndex = size - 1;
if(elementIndex < minIndex || elementIndex > maxIndex) {
return Optional.empty();
}
return Optional.of(elements.get(elementIndex));
}
@Override
public Can<T> sorted(final @NonNull Comparator<? super T> c) {
var newElements = _Lists.<T>newArrayList(elements);
newElements.sort(c);
return new Can_Multiple<>(newElements);
}
@Override
public Can<T> distinct() {
var set = new LinkedHashSet<T>(); // preserve order
set.addAll(elements);
return Can.ofCollection(set);
}
@Override
public Can<T> distinct(final @NonNull BiPredicate<T, T> equality) {
final int initialSize = Math.min(1024, elements.size());
var uniqueElements = _Lists.<T>newArrayList(initialSize);
elements
.forEach(element->{
if(!uniqueElements.stream().anyMatch(x->equality.test(x, element))) {
uniqueElements.add(element);
}
});
return _CanFactory.ofNonNullElements(uniqueElements);
}
@Override
public Iterator<T> iterator(final int skip, final int limit) {
return Collections.unmodifiableList(elements).stream()
.skip(skip)
.limit(limit)
.iterator();
}
@Override
public Iterator<T> iterator() {
return Collections.unmodifiableList(elements).iterator();
}
@Override
public Iterator<T> reverseIterator() {
return new Iterator<T>() {
private int remainingCount = size();
@Override public boolean hasNext() { return remainingCount>0; }
@Override public T next() {
if(!hasNext()) { throw _Exceptions.noSuchElement(); }
return elements.get(--remainingCount);
}
};
}
@Override
public Can<T> reverse() {
var reverse = new ArrayList<T>(elements.size());
for(int i=elements.size()-1; i>=0; --i) {
reverse.add(elements.get(i));
}
return new Can_Multiple<>(reverse);
}
@Override
public Can<T> reduce(final @NonNull BinaryOperator<T> accumulator) {
return this.stream().reduce(accumulator)
.<Can<T>>map(Can_Singleton::new)
.orElseGet(Can::empty);
}
@Override
public void forEach(final @NonNull Consumer<? super T> action) {
elements.forEach(action);
}
@Override
public Can<T> filter(final @Nullable Predicate<? super T> predicate) {
if(predicate==null) {
return this; // identity
}
var filteredElements =
stream()
.filter(predicate)
.collect(Collectors.toCollection(ArrayList::new));
// optimization for the case when the filter accepted all
if(filteredElements.size()==size()) {
return this; // identity
}
return Can.ofCollection(filteredElements);
}
@Override
public <R> void zip(final @NonNull Iterable<R> zippedIn, final @NonNull BiConsumer<? super T, ? super R> action) {
var zippedInIterator = zippedIn.iterator();
stream().forEach(t->{
action.accept(t, zippedInIterator.next());
});
}
@Override
public <R, Z> Can<R> zipMap(final @NonNull Iterable<Z> zippedIn, final @NonNull BiFunction<? super T, ? super Z, R> mapper) {
var zippedInIterator = zippedIn.iterator();
return map(t->mapper.apply(t, zippedInIterator.next()));
}
@Override
public <R, Z> Stream<R> zipStream(final @NonNull Iterable<Z> zippedIn, final BiFunction<? super T, ? super Z, R> mapper) {
var zippedInIterator = zippedIn.iterator();
return stream()
.map(t->mapper.apply(t, zippedInIterator.next()))
.filter(_NullSafe::isPresent);
}
@Override
public Can<T> add(final @Nullable T element) {
return element!=null
? Can.ofStream(Stream.concat(elements.stream(), Stream.of(element))) // append
: this;
}
@Override
public Can<T> addAll(final @Nullable Can<T> other) {
if(other==null
|| other.isEmpty()) {
return this;
}
var newElements = new ArrayList<T>(this.size() + other.size());
newElements.addAll(elements);
other.forEach(newElements::add);
return new Can_Multiple<>(newElements);
}
@Override
public Can<T> add(final int index, final @Nullable T element) {
if(element==null) {
return this; // identity
}
var newElements = new ArrayList<T>(elements);
newElements.add(index, element);
return Can.ofCollection(newElements);
}
@Override
public Can<T> replace(final int index, final @Nullable T element) {
if(element==null) {
return remove(index);
}
var newElements = new ArrayList<T>(elements);
newElements.set(index, element);
return Can.ofCollection(newElements);
}
@Override
public Can<T> remove(final int index) {
var newElements = new ArrayList<T>(elements);
newElements.remove(index);
return Can.ofCollection(newElements);
}
@Override
public Can<T> remove(final @Nullable T element) {
if(element==null) {
return this; // identity
}
var newElements = new ArrayList<T>(elements);
newElements.remove(element);
return Can.ofCollection(newElements);
}
@Override
public Can<T> pickByIndex(final @Nullable int... indices) {
if(indices==null
||indices.length==0) {
return Can.empty();
}
var newElements = new ArrayList<T>(indices.length);
final int maxIndex = size()-1;
for(int index:indices) {
if(index>=0
&& index<=maxIndex) {
newElements.add(elements.get(index));
}
}
return Can.ofCollection(newElements);
}
@Override
public Can<T> pickByIndex(final @Nullable IntStream intStream) {
if(intStream==null) {
return Can.empty();
}
var newElements = new ArrayList<T>();
final int maxIndex = size()-1;
intStream
.filter(index->index>=0 && index<=maxIndex)
.forEach(index->{
newElements.add(elements.get(index));
});
return _CanFactory.ofNonNullElements(newElements);
}
@Override
public Can<T> subCan(final int startInclusive) {
return pickByIndex(IntStream.range(startInclusive, size()));
}
@Override
public Can<T> subCan(final int startInclusive, final int endExclusive) {
final int upperBoundExclusive = endExclusive < 0
? size() + endExclusive
: endExclusive;
if (startInclusive >= upperBoundExclusive) {
return Can.empty();
}
return pickByIndex(IntStream.range(startInclusive, upperBoundExclusive));
}
@Override
public Can<Can<T>> partitionInnerBound(final int maxInnerSize) {
if(maxInnerSize<1) {
throw _Exceptions.illegalArgument("maxInnerSize %d must be greater or equal to 1", maxInnerSize);
}
final int n = size();
final int subCanCount = (n - 1)/maxInnerSize + 1;
var newElements = new ArrayList<Can<T>>(subCanCount);
for(int i=0; i<n; i+=maxInnerSize) {
newElements.add(subCan(i, i + maxInnerSize)); // index overflow is ignored
}
return _CanFactory.ofNonNullElements(newElements);
}
@Override
public Can<Can<T>> partitionOuterBound(final int outerSizeYield) {
if(outerSizeYield<1) {
throw _Exceptions.illegalArgument("outerSizeYield %d must be greater or equal to 1", outerSizeYield);
}
final int n = size();
final int maxInnerSize = (n - 1)/outerSizeYield + 1;
return partitionInnerBound(maxInnerSize);
}
@Override
public int indexOf(final @Nullable T element) {
return this.elements.indexOf(element);
}
@Override
public boolean anyMatch(final Predicate<? super T> predicate) {
// equivalent to stream().anyMatch(predicate); hoping the loop is faster..
for(var el : elements) if(predicate.test(el)) return true;
return false;
}
@Override
public boolean allMatch(final Predicate<? super T> predicate) {
// equivalent to stream().allMatch(predicate); hoping the loop is faster..
for(var el : elements) if(!predicate.test(el)) return false;
return true;
}
@Override
public String toString() {
var literal = stream()
.map(s->""+s)
.collect(Collectors.joining(", "));
return "Can["+literal+"]";
}
@Override
public boolean equals(final Object obj) {
if(obj instanceof Can) {
return ((Can<?>) obj).isEqualTo(this);
}
return false;
}
@Override
public int hashCode() {
return elements.hashCode();
}
@Override
public int compareTo(final @Nullable Can<T> other) {
// when returning
// -1 ... this (multi-can) is before other
// +1 ... this (multi-can) is after other
if(other==null
|| other.isEmpty()) {
return 1; // all empty Cans are same and come first
}
if(other.isCardinalityOne()) {
final int firstElementComparison = _Objects.compareNonNull(
this.elements.get(0),
other.getSingletonOrFail());
if(firstElementComparison!=0) {
return firstElementComparison;
}
}
// at this point firstElementComparison is 0 and other is a multi-can
// XXX we already compared the first elements, could skip ahead for performance reasons
if(this.size()>=other.size()) {
var otherIterator = other.iterator();
for(T left: this) {
if(!otherIterator.hasNext()) {
return 1; // the other has fewer elements hence comes first
}
var right = otherIterator.next();
int c = _Objects.compareNonNull(left, right);
if(c!=0) {
return c;
}
}
} else {
var thisIterator = this.iterator();
for(T right: other) {
if(!thisIterator.hasNext()) {
return -1; // this has fewer elements hence comes first
}
var left = thisIterator.next();
int c = _Objects.compareNonNull(left, right);
if(c!=0) {
return c;
}
}
}
return 0; // we compared all elements and found no difference
}
@Override
public List<T> toList() {
return Collections.unmodifiableList(elements); // serializable and immutable
}
@Override
public List<T> toArrayList() {
return _Lists.newArrayList(elements);
}
@Override
public Set<T> toSet() {
var set = _Sets.<T>newHashSet(); // serializable
set.addAll(elements);
return Collections.unmodifiableSet(set); // serializable and immutable
}
@Override
public Set<T> toSet(final @NonNull Consumer<T> onDuplicated) {
var set = _Sets.<T>newHashSet(); // serializable
elements
.forEach(s->{
if(!set.add(s)) {
onDuplicated.accept(s);
}
});
return Collections.unmodifiableSet(set); // serializable and immutable
}
@Override
public T[] toArray(final @NonNull Class<T> elementType) {
var array = _Casts.<T[]>uncheckedCast(Array.newInstance(elementType, size()));
return elements.toArray(array);
}
@Override
public <K> Map<K, T> toMap(
final @NonNull Function<? super T, ? extends K> keyExtractor) {
Map<K, T> map = collect(Collectors.toMap(keyExtractor, UnaryOperator.identity()));
return Collections.unmodifiableMap(map);
}
@Override
public <K, M extends Map<K, T>> Map<K, T> toMap(
final @NonNull Function<? super T, ? extends K> keyExtractor,
final @NonNull BinaryOperator<T> mergeFunction,
final @NonNull Supplier<M> mapFactory) {
Map<K, T> map = collect(Collectors.toMap(
keyExtractor, UnaryOperator.identity(), mergeFunction, mapFactory));
return Collections.unmodifiableMap(map);
}
@Override
public <R, A> R collect(final @NonNull Collector<? super T, A, R> collector) {
return stream().collect(collector);
}
@Override
public <K> Map<K, Can<T>> groupBy(
final @NonNull Function<? super T, ? extends K> classifier) {
return groupBy(classifier, HashMap::new);
}
@Override
public <K, M extends Map<K, Can<T>>> Map<K, Can<T>> groupBy(
final @NonNull Function<? super T, ? extends K> classifier,
final @NonNull Supplier<M> mapFactory) {
var map = collect(Collectors.groupingBy(classifier, mapFactory, Can.toCan()));
return Collections.unmodifiableMap(map);
}
@Override
public String join(final @NonNull String delimiter) {
return join(Object::toString, delimiter);
}
@Override
public String join(final @NonNull Function<? super T, String> toStringFunction, final @NonNull String delimiter) {
return stream()
.map(toStringFunction)
.filter(_NullSafe::isPresent)
.collect(Collectors.joining(delimiter));
}
}