Przeglądaj źródła

new: CQuery #chunk
fix: CQSlice #iterator - performance
new: IntRange #of #empty #withLimit
new: IntSet #from #of
new: MathUtils #divceil

Ranides Atterwim 5 miesięcy temu
rodzic
commit
2e6e7dd168

+ 27 - 1
assira.core/src/main/java/net/ranides/assira/collection/lists/IntRange.java

@@ -21,7 +21,9 @@ import net.ranides.assira.collection.sets.IntSet;
  * @author Ranides Atterwim {@literal <ranides@gmail.com>}
  */
 public class IntRange extends AIntList implements IntSet, RandomAccess {
-    
+
+    private static final IntRange EMPTY = new IntRange(0,0);
+
     private static final String E_IMMUTABLE = "Immutable.";
     
     private final int begin;
@@ -34,10 +36,34 @@ public class IntRange extends AIntList implements IntSet, RandomAccess {
      * @param end end
      */
     public IntRange(int begin, int end) {
+        if(end < begin) {
+            throw new IllegalArgumentException("Invalid range: ["+begin + "," + end + ")");
+        }
         this.begin = begin;
         this.end = end;
     }
 
+    public static IntRange empty() {
+        return EMPTY;
+    }
+
+    public static IntRange of(int begin, int end) {
+        return new IntRange(begin, end);
+    }
+
+    public IntRange withLimit(int begin, int end) {
+        if(end < begin) {
+            throw new IllegalArgumentException("Invalid limit: ["+begin + "," + end + ")");
+        }
+        int min = Math.max(this.begin, begin);
+        int max = Math.min(this.end, end);
+        return max <= min ? empty() : new IntRange(min, max);
+    }
+
+    public IntRange withLimit(IntRange range) {
+        return withLimit(range.begin, range.end);
+    }
+
     @Override
     public int size() {
         return end - begin;

+ 8 - 0
assira.core/src/main/java/net/ranides/assira/collection/query/CQuery.java

@@ -326,6 +326,13 @@ public interface CQuery<T> extends Iterable<T> {
      */
     <K,V> Map<K,V> group(Function<? super T, ? extends K> key, Function<? super T, V> value);
 
+    /**
+     * Returns query with content chunked into groups of given "size".
+     * Last element may contain less elements.
+     * @param size
+     * @return
+     */
+    CQuery<CQuery<T>> chunk(int size);
 
     /**
      * Checks if stream is empty.
@@ -1102,6 +1109,7 @@ public interface CQuery<T> extends Iterable<T> {
             return EMPTY;
         }
 
+        @SuppressWarnings("unchecked")
         public final <T extends F> CQuery<T> empty(Supplier<? extends RuntimeException> message) {
             return EMPTY.message(message);
         }

+ 5 - 0
assira.core/src/main/java/net/ranides/assira/collection/query/CQueryAbstract.java

@@ -236,6 +236,11 @@ public abstract class CQueryAbstract<T> implements CQuery<T>, CQueryFeatures {
         return (CQArray<T>)new CQArray<>(data).parallel(isParallel());
     }
 
+    @Override
+    public CQuery<CQuery<T>> chunk(int size) {
+        return new CQChunk<>(this, size);
+    }
+
     @Override
     public CQuery<T> limit(Predicate<? super T> p) {
         return new CQLimit<>(this, p);

+ 126 - 0
assira.core/src/main/java/net/ranides/assira/collection/query/derived/CQChunk.java

@@ -0,0 +1,126 @@
+package net.ranides.assira.collection.query.derived;
+
+import net.ranides.assira.collection.lists.ListUtils;
+import net.ranides.assira.collection.query.CQuery;
+import net.ranides.assira.collection.query.CQueryAbstract;
+import net.ranides.assira.collection.query.base.CQList;
+import net.ranides.assira.collection.query.support.BaseList;
+import net.ranides.assira.functional.Predicates;
+import net.ranides.assira.math.MathUtils;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.function.Supplier;
+import java.util.stream.Stream;
+
+/**
+ * Adapter query: it returns only specified fragment of source query.
+ * Used by {@link CQuery#slice(int, int)} operation.
+ *
+ * It reports all features supported by source except "each".
+ * Fast "each" is reported only, if selected slice does not skip more elements than "FAST_SKIP_LIMIT"
+ *
+ * @param <T> T
+ */
+public class CQChunk<T> extends CQueryAbstract<CQuery<T>> {
+
+    private final CQuery<T> source;
+    private final int chunk;
+
+    /**
+     * Creates new chunked stream.
+     *
+     * @param source source
+     * @param size size
+     */
+    public CQChunk(CQuery<T> source, int size) {
+        this.source = source;
+        this.chunk = size;
+    }
+
+    @Override
+    public Supplier<? extends RuntimeException> message() {
+        return source.message();
+    }
+
+    @Override
+    public CQuery<CQuery<T>> message(Supplier<? extends RuntimeException> message) {
+        source.message(message);
+        return this;
+    }
+
+    @Override
+    public boolean hasFastEach() {
+        return source.features().hasFastEach();
+    }
+
+    @Override
+    public boolean hasFastStream() {
+        return hasFastList();
+    }
+
+    @Override
+    public boolean hasFastIterator() {
+        return hasFastList();
+    }
+
+    @Override
+    public boolean hasFastLength() {
+        return source.features().hasFastLength() || source.features().hasFastList();
+    }
+
+    @Override
+    public boolean hasFastList() {
+        return source.features().hasFastList();
+    }
+
+    @Override
+    public boolean isParallel() {
+        return source.features().isParallel();
+    }
+
+    @Override
+    public CQuery<CQuery<T>> parallel() {
+        return isParallel() ? this : new CQChunk<>(source.parallel(), chunk);
+    }
+
+    @Override
+    public CQuery<CQuery<T>> sequential() {
+        return isParallel() ? new CQChunk<>(source.sequential(), chunk) : this;
+    }
+
+    @Override
+    public Iterator<CQuery<T>> iterator() {
+        return list().iterator();
+    }
+
+    @Override
+    public Stream<CQuery<T>> stream() {
+        return list().stream();
+    }
+
+    @Override
+    public List<CQuery<T>> list() {
+        if(hasFastList()) {
+            List<T> list = source.list();
+            int size = list.size();
+            return ListUtils.produce(MathUtils.divceil(size, chunk), i -> {
+                return new CQList<>(list.subList(i * chunk, Math.min(size, i * chunk + chunk)));
+            });
+        } else {
+            int max = size();
+            return ListUtils.produce(max, i -> source.slice(i*chunk, i*chunk+chunk));
+        }
+    }
+
+    @Override
+    public int size() {
+        return MathUtils.divceil(source.size(), chunk);
+    }
+
+    @Override
+    public boolean whileEach(Predicates.EachPredicate<? super CQuery<T>> consumer) {
+        return BaseList.whileEach(this, consumer);
+    }
+
+}

+ 5 - 1
assira.core/src/main/java/net/ranides/assira/collection/query/derived/CQSlice.java

@@ -95,7 +95,11 @@ public class CQSlice<T> extends CQueryAbstract<T> {
 
     @Override
     public Iterator<T> iterator() {
-        return IteratorUtils.limit(source.iterator(), begin, end);
+        if(hasFastList()) {
+            return IteratorUtils.limit(list().listIterator(begin), end-begin);
+        } else {
+            return IteratorUtils.limit(source.iterator(), begin, end);
+        }
     }
 
     @Override

+ 8 - 0
assira.core/src/main/java/net/ranides/assira/collection/sets/IntSet.java

@@ -18,4 +18,12 @@ import net.ranides.assira.collection.iterators.IntIterator;
  */
 public interface IntSet extends IntCollection, Set<Integer> {
 
+    static IntSet from(IntCollection input) {
+        return new IntOpenSet(input);
+    }
+
+    static IntSet of(int... values) {
+        return new IntOpenSet(values);
+    }
+
 }

+ 28 - 0
assira.core/src/main/java/net/ranides/assira/math/MathUtils.java

@@ -470,5 +470,33 @@ public final class MathUtils {
         return clip(adds(x,y), min, max);
     }
 
+    /**
+     * rounded up division
+     * @param a a
+     * @param b b
+     * @return ceil(a/b)
+     */
+    public static long divceil(long a, long b) {
+        long out = a / b;
+        if ((a ^ b) > 0 && a % b != 0){
+            out++;
+        }
+        return out;
+    }
+
+    /**
+     * rounded up division
+     * @param a a
+     * @param b b
+     * @return ceil(a/b)
+     */
+    public static int divceil(int a, int b) {
+        int out = a / b;
+        if ((a ^ b) > 0 && a % b != 0){
+            out++;
+        }
+        return out;
+    }
+
 }
 

+ 4 - 0
assira.core/src/main/java/net/ranides/assira/reflection/IConstructor.java

@@ -56,9 +56,13 @@ public interface IConstructor<T> extends IExecutable {
      * It applies especially for enum types. Creation new instances of enum is extremally strange,
      * but possible if you use accessor
      *
+     * Acquiring accessor is really stupid idea. This method should be moved into
+     * assira.lambda : net.ranides.assira.reflection.ConstructorUtils
+     *
      * @return AnyFunction
      */
     @Meta.UnstableAPI
+    @Meta.ForJDK8
     AnyFunction<T> accessor();
 
 }

+ 28 - 0
assira.core/src/test/java/net/ranides/assira/collection/query/derived/CQChunkTest.java

@@ -0,0 +1,28 @@
+package net.ranides.assira.collection.query.derived;
+
+import net.ranides.assira.collection.query.CQuery;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+public class CQChunkTest {
+
+    @Test
+    public void sanity() {
+        List<List<Integer>> list = CQuery
+            .from(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14))
+            .chunk(4)
+            .map(v -> v.list())
+            .list();
+        assertEquals(
+            Arrays.asList(
+                Arrays.asList(1,2,3,4),
+                Arrays.asList(5,6,7,8),
+                Arrays.asList(9,10,11,12),
+                Arrays.asList(13,14)
+            ), list);
+    }
+}

+ 22 - 3
assira.core/src/test/java/net/ranides/assira/reflection/impl/RConstructorTest.java

@@ -25,7 +25,6 @@ import static net.ranides.assira.junit.NewAssert.*;
 
 import net.ranides.test.mockup.reflection.ForConstructor.SomeAnnotation;
 import net.ranides.assira.reflection.util.AnnotationUtils;
-import org.junit.Ignore;
 
 /**
  *
@@ -129,8 +128,28 @@ public class RConstructorTest {
     }
 
     @Test
-    public void testAccessor() {
-        Assume.assumeTrue(RuntimeUtils.getJavaVersion() < 19);
+    public void testAccessorPublic() {
+        Assume.assumeTrue(RuntimeUtils.getJavaVersion() < 11);
+
+        AnyFunction<?> accessor = ic.constructors()
+            .require(IArguments.typeinfo(int.class, int.class, int.class))
+            .first()
+            .get()
+            .accessor();
+
+        Object exp1 = new ForConstructor.Record(1, 2, 3);
+        Object var1 = accessor.call(1, 2, 3);
+
+        assertEquals(exp1.toString(), var1.toString());
+        assertEquals(exp1.getClass(), var1.getClass());
+    }
+
+    @Test
+    public void testAccessorEnum() {
+        Assume.assumeTrue(RuntimeUtils.getJavaVersion() < 11);
+
+        // this test is relly really stupid and incompatible with many JDKs
+        // let's think if we want to use this at all
 
         // we search for constructors of "enum" types
         // although it is quite strange behaviour, we do this intentionally because