You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@causeway.apache.org by ah...@apache.org on 2023/03/09 13:01:01 UTC
[causeway] branch master updated: CAUSEWAY-3304: [Commons] Can: adding methods for sub-list and partition
This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/causeway.git
The following commit(s) were added to refs/heads/master by this push:
new e888393319 CAUSEWAY-3304: [Commons] Can: adding methods for sub-list and partition
e888393319 is described below
commit e8883933194057b13f2eb078f3220d2725be139d
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Mar 9 14:00:56 2023 +0100
CAUSEWAY-3304: [Commons] Can: adding methods for sub-list and partition
---
.../apache/causeway/commons/collections/Can.java | 57 ++++++++++++++++++++
.../causeway/commons/collections/Can_Empty.java | 21 ++++++++
.../causeway/commons/collections/Can_Multiple.java | 48 +++++++++++++++++
.../commons/collections/Can_Singleton.java | 53 +++++++++++++++++++
.../causeway/commons/collections/CanTest.java | 61 ++++++++++++++++++++++
5 files changed, 240 insertions(+)
diff --git a/commons/src/main/java/org/apache/causeway/commons/collections/Can.java b/commons/src/main/java/org/apache/causeway/commons/collections/Can.java
index 3004042630..dcacf60eed 100644
--- a/commons/src/main/java/org/apache/causeway/commons/collections/Can.java
+++ b/commons/src/main/java/org/apache/causeway/commons/collections/Can.java
@@ -37,6 +37,7 @@ import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collectors;
+import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.springframework.lang.Nullable;
@@ -517,10 +518,66 @@ extends ImmutableCollection<T>, Comparable<Can<T>>, Serializable {
* )
* </pre>
* (where nulls are being ignored)
+ * <p>
+ * In other words: Out of bounds picking is simply ignored.
* @param indices - null-able
*/
Can<T> pickByIndex(@Nullable int ...indices);
+ /**
+ * Returns a {@link Can} that is made of the elements from this {@link Can},
+ * picked by index using the given {@link IntStream} (in the order of picking).
+ * <p>
+ * Out of bounds picking is simply ignored.
+ */
+ Can<T> pickByIndex(@Nullable IntStream intStream);
+
+ // -- SUB SETS AND PARTITIONS
+
+ /**
+ * Returns a sub-{@link Can} that is made of elements from this {@link Can},
+ * when selected by those indices,
+ * that result from given range {@code[startInclusive, endExclusive)}.
+ * <p>
+ * Out of bounds picking is simply ignored.
+ *
+ * @param startInclusive the (inclusive) initial index
+ * @param endExclusive the exclusive upper bound index
+ */
+ Can<T> subCan(int startInclusive, int endExclusive);
+
+ /**
+ * Returns consecutive {@link #subCan(int, int) subCan},
+ * each of the same maxInnerSize, while the final {@link Can} may be smaller.
+ * <p>
+ * For example,
+ * partitioning a {@link Can} containing {@code [a, b, c, d, e]} with a partition
+ * size of 3 yields {@code [[a, b, c], [d, e]]} -- an outer {@link Can} containing
+ * two inner {@link Can}s of three and two elements, all in the original order.
+ *
+ * @param maxInnerSize
+ * the desired size of each sub-{@link Can}s (the last may be smaller)
+ * @return a {@link Can} of consecutive sub-{@link Can}s
+ * @apiNote an alternative approach would be to distribute inner sizes as fair as possible,
+ * but this method does not
+ */
+ Can<Can<T>> partitionInnerBound(int maxInnerSize);
+
+ /**
+ * Tries to split this {@link Can} into outerSizeYield consecutive {@link #subCan(int, int) subCan},
+ * each of the same calculated max-inner-size, while the final {@link Can} may be smaller.
+ * <p>
+ * An outer cardinality of outerSizeYield is either exactly met or under-represented,
+ * based on how many elements are actually available.
+ *
+ * @param outerSizeYield
+ * the desired number of sub-{@link Can}s
+ * @return a {@link Can} of consecutive sub-{@link Can}s
+ * @apiNote an alternative approach would be to distribute inner sizes as fair as possible,
+ * but this method does not
+ */
+ Can<Can<T>> partitionOuterBound(int outerSizeYield);
+
// -- SEARCH
/**
diff --git a/commons/src/main/java/org/apache/causeway/commons/collections/Can_Empty.java b/commons/src/main/java/org/apache/causeway/commons/collections/Can_Empty.java
index a95715293b..81c28ae7d7 100644
--- a/commons/src/main/java/org/apache/causeway/commons/collections/Can_Empty.java
+++ b/commons/src/main/java/org/apache/causeway/commons/collections/Can_Empty.java
@@ -32,6 +32,7 @@ import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
+import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.springframework.lang.Nullable;
@@ -200,6 +201,26 @@ final class Can_Empty<T> implements Can<T> {
return Can.empty();
}
+ @Override
+ public Can<T> pickByIndex(final @Nullable IntStream intStream) {
+ return Can.empty();
+ }
+
+ @Override
+ public Can<T> subCan(final int startInclusive, final int endExclusive) {
+ return Can.empty();
+ }
+
+ @Override
+ public Can<Can<T>> partitionInnerBound(final int maxInnerSize) {
+ return Can.empty();
+ }
+
+ @Override
+ public Can<Can<T>> partitionOuterBound(final int outerSizeYield) {
+ return Can.empty();
+ }
+
@Override
public int indexOf(final @Nullable T element) {
return -1;
diff --git a/commons/src/main/java/org/apache/causeway/commons/collections/Can_Multiple.java b/commons/src/main/java/org/apache/causeway/commons/collections/Can_Multiple.java
index f43f462466..e4d4a78774 100644
--- a/commons/src/main/java/org/apache/causeway/commons/collections/Can_Multiple.java
+++ b/commons/src/main/java/org/apache/causeway/commons/collections/Can_Multiple.java
@@ -35,6 +35,7 @@ import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
+import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.springframework.lang.Nullable;
@@ -286,6 +287,53 @@ final class Can_Multiple<T> implements Can<T> {
return Can.ofCollection(newElements);
}
+ @Override
+ public Can<T> pickByIndex(final @Nullable IntStream intStream) {
+ if(intStream==null) {
+ return Can.empty();
+ }
+ val 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, final int endExclusive) {
+ if (startInclusive >= endExclusive) {
+ return Can.empty();
+ }
+ return pickByIndex(IntStream.range(startInclusive, endExclusive));
+ }
+
+ @Override
+ public Can<Can<T>> partitionInnerBound(final int maxInnerSize) {
+ if(maxInnerSize<1) {
+ throw _Exceptions.illegalArgument("maxInnerSize %d must be grater or equal to 1", maxInnerSize);
+ }
+ final int n = size();
+ final int subCanCount = (n - 1)/maxInnerSize + 1;
+ val 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 grater 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);
diff --git a/commons/src/main/java/org/apache/causeway/commons/collections/Can_Singleton.java b/commons/src/main/java/org/apache/causeway/commons/collections/Can_Singleton.java
index 0103f1f755..8e10a40415 100644
--- a/commons/src/main/java/org/apache/causeway/commons/collections/Can_Singleton.java
+++ b/commons/src/main/java/org/apache/causeway/commons/collections/Can_Singleton.java
@@ -34,6 +34,7 @@ import java.util.function.BiPredicate;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
+import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.springframework.lang.Nullable;
@@ -252,6 +253,58 @@ final class Can_Singleton<T> implements Can<T> {
return _CanFactory.ofNonNullElements(newElements);
}
+ @Override
+ public Can<T> pickByIndex(final @Nullable IntStream intStream) {
+ if(intStream==null) {
+ return Can.empty();
+ }
+ final long pickCountL = intStream.filter(index->index==0).count();
+ if(pickCountL==0L) {
+ return Can.empty();
+ }
+ if(pickCountL==1L) {
+ return this;
+ }
+ if(pickCountL>Integer.MAX_VALUE) {
+ throw _Exceptions.illegalArgument("pickCount %d is too large to fit into an int", pickCountL);
+ }
+ final int pickCount = (int) pickCountL;
+ val newElements = new ArrayList<T>(pickCount);
+ for(int i=0; i<pickCount; i++) {
+ newElements.add(element);
+ }
+ return _CanFactory.ofNonNullElements(newElements);
+ }
+
+ @Override
+ public Can<T> subCan(final int startInclusive, final int endExclusive) {
+ if (startInclusive >= endExclusive) {
+ return Can.empty();
+ }
+ return (startInclusive<=0
+ && endExclusive>0)
+ ? this
+ : Can.empty();
+ }
+
+ @Override
+ public Can<Can<T>> partitionInnerBound(final int maxInnerSize) {
+ if(maxInnerSize<1) {
+ throw _Exceptions.illegalArgument("maxInnerSize %d must be grater or equal to 1", maxInnerSize);
+ }
+ // a singular always fits into a single slot
+ return Can.of(this);
+ }
+
+ @Override
+ public Can<Can<T>> partitionOuterBound(final int outerSizeYield) {
+ if(outerSizeYield<1) {
+ throw _Exceptions.illegalArgument("outerSizeYield %d must be grater or equal to 1", outerSizeYield);
+ }
+ // a singular always fits into a single slot
+ return Can.of(this);
+ }
+
@Override
public int indexOf(final @Nullable T element) {
return this.element.equals(element) ? 0 : -1;
diff --git a/commons/src/test/java/org/apache/causeway/commons/collections/CanTest.java b/commons/src/test/java/org/apache/causeway/commons/collections/CanTest.java
index f069019b53..d8ef8de3a6 100644
--- a/commons/src/test/java/org/apache/causeway/commons/collections/CanTest.java
+++ b/commons/src/test/java/org/apache/causeway/commons/collections/CanTest.java
@@ -242,6 +242,67 @@ class CanTest {
}
+ @Test
+ void partitioningInner_whenLastIsSmaller() {
+ final Can<Integer> origin = Can.of(1, 2, 3, 4, 5, 6, 7, 8);
+ final Can<Can<Integer>> subCans = origin.partitionInnerBound(3);
+ assertEquals(3, subCans.size());
+ assertEquals(Can.of(1, 2, 3),
+ subCans.getElseFail(0));
+ assertEquals(Can.of(4, 5, 6),
+ subCans.getElseFail(1));
+ assertEquals(Can.of(7, 8),
+ subCans.getElseFail(2));
+ }
+
+ @Test
+ void partitioningInner_whenLastIsFull() {
+ final Can<Integer> origin = Can.of(1, 2, 3, 4, 5, 6, 7, 8, 9);
+ final Can<Can<Integer>> subCans = origin.partitionInnerBound(3);
+ assertEquals(3, subCans.size());
+ assertEquals(Can.of(1, 2, 3),
+ subCans.getElseFail(0));
+ assertEquals(Can.of(4, 5, 6),
+ subCans.getElseFail(1));
+ assertEquals(Can.of(7, 8, 9),
+ subCans.getElseFail(2));
+ }
+
+ @Test
+ void partitioningOuter_whenEvenlySized() {
+ final Can<Integer> origin = Can.of(1, 2, 3, 4, 5, 6);
+ final Can<Can<Integer>> subCans = origin.partitionOuterBound(2);
+ assertEquals(2, subCans.size());
+ assertEquals(Can.of(1, 2, 3),
+ subCans.getElseFail(0));
+ assertEquals(Can.of(4, 5, 6),
+ subCans.getElseFail(1));
+ }
+
+ @Test
+ void partitioningOuter_whenUnevenlySized() {
+ final Can<Integer> origin = Can.of(1, 2, 3, 4, 5);
+ final Can<Can<Integer>> subCans = origin.partitionOuterBound(2);
+ assertEquals(2, subCans.size());
+ assertEquals(Can.of(1, 2, 3),
+ subCans.getElseFail(0));
+ assertEquals(Can.of(4, 5),
+ subCans.getElseFail(1));
+ }
+
+ @Test
+ void partitioningOuter_whenUnderRepresented() {
+ final Can<Integer> origin = Can.of(1, 2, 3);
+ final Can<Can<Integer>> subCans = origin.partitionOuterBound(5);
+ assertEquals(3, subCans.size());
+ assertEquals(Can.of(1),
+ subCans.getElseFail(0));
+ assertEquals(Can.of(2),
+ subCans.getElseFail(1));
+ assertEquals(Can.of(3),
+ subCans.getElseFail(2));
+ }
+
// -- HEPER