diff --git a/src/main/java/com/bestvike/Index.java b/src/main/java/com/bestvike/Index.java
new file mode 100644
index 00000000..2211960a
--- /dev/null
+++ b/src/main/java/com/bestvike/Index.java
@@ -0,0 +1,99 @@
+package com.bestvike;
+
+import com.bestvike.linq.exception.ExceptionArgument;
+import com.bestvike.linq.exception.ThrowHelper;
+
+/**
+ * Represent a type can be used to index a collection either from the start or the end.
+ *
+ *
+ * {@code
+ * List list = Arrays.asList(1, 2, 3, 4, 5);
+ * Integer lastElement = Linq.of(list).elementAt(Index.fromEnd(1)); // lastElement = 5
+ * }
+ *
+ *
+ * Created by 许崇雷 on 2023-05-31.
+ */
+public final class Index {
+ public static final Index Start = new Index(0);
+ public static final Index End = new Index(~0);
+ private final int value;
+
+ // The following private constructors mainly created for perf reason to avoid the checks
+ private Index(int value) {
+ this.value = value;
+ }
+
+ public Index(int value, boolean fromEnd) {
+ if (value < 0)
+ ThrowHelper.throwArgumentOutOfRangeException(ExceptionArgument.value);
+ if (fromEnd) {
+ this.value = ~value;
+ } else {
+ this.value = value;
+ }
+ }
+
+ public static Index fromStart(int value) {
+ if (value < 0)
+ ThrowHelper.throwArgumentOutOfRangeException(ExceptionArgument.value);
+ return new Index(value);
+ }
+
+ public static Index fromEnd(int value) {
+ if (value < 0)
+ ThrowHelper.throwArgumentOutOfRangeException(ExceptionArgument.value);
+ return new Index(~value);
+ }
+
+ public int getValue() {
+ if (this.value < 0)
+ return ~this.value;
+ else
+ return this.value;
+ }
+
+ public boolean isFromEnd() {
+ return this.value < 0;
+ }
+
+ public int getOffset(int length) {
+ int offset = this.value;
+ if (this.isFromEnd()) {
+ offset += length + 1;
+ }
+ return offset;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof Index) {
+ return this.value == ((Index) obj).value;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return this.value;
+ }
+
+ @Override
+ @SuppressWarnings("MethodDoesntCallSuperMethod")
+ protected Index clone() {
+ return new Index(this.value);
+ }
+
+ @Override
+ public String toString() {
+ if (this.isFromEnd()) {
+ return this.toStringFromEnd();
+ }
+ return String.valueOf(this.value);
+ }
+
+ private String toStringFromEnd() {
+ return '^' + String.valueOf(this.getValue());
+ }
+}
diff --git a/src/main/java/com/bestvike/Range.java b/src/main/java/com/bestvike/Range.java
new file mode 100644
index 00000000..d0a73dc2
--- /dev/null
+++ b/src/main/java/com/bestvike/Range.java
@@ -0,0 +1,106 @@
+package com.bestvike;
+
+import com.bestvike.linq.exception.ExceptionArgument;
+import com.bestvike.linq.exception.ThrowHelper;
+
+/**
+ * Represent a range has start and end indexes.
+ *
+ *
+ * {@code
+ * List someArray = Arrays.asList(1, 2, 3, 4, 5);
+ * Integer[] subArray1 = Linq.of(someArray).take(Range.endAt(Index.fromStart(2))).toArray(Integer.class); // { 1, 2 }
+ * Integer[] subArray2 = Linq.of(someArray).take(Range.startAt(Index.fromStart(1))).toArray(Integer.class); // { 2, 3, 4, 5 }
+ * }
+ *
+ *
+ * Created by 许崇雷 on 2023-05-31.
+ */
+public final class Range {
+ public static final Range All = new Range(Index.Start, Index.End);
+ private final Index start;
+ private final Index end;
+
+ public Range(Index start, Index end) {
+ if (start == null)
+ ThrowHelper.throwArgumentNullException(ExceptionArgument.start);
+ if (end == null)
+ ThrowHelper.throwArgumentNullException(ExceptionArgument.end);
+ this.start = start;
+ this.end = end;
+ }
+
+ public static Range startAt(Index start) {
+ return new Range(start, Index.End);
+ }
+
+ public static Range endAt(Index end) {
+ return new Range(Index.Start, end);
+ }
+
+ public Index getStart() {
+ return this.start;
+ }
+
+ public Index getEnd() {
+ return this.end;
+ }
+
+ public OffsetLength getOffsetAndLength(int length) {
+ int start;
+ Index startIndex = this.getStart();
+ if (startIndex.isFromEnd())
+ start = length - startIndex.getValue();
+ else
+ start = startIndex.getValue();
+
+ int end;
+ Index endIndex = this.getEnd();
+ if (endIndex.isFromEnd())
+ end = length - endIndex.getValue();
+ else
+ end = endIndex.getValue();
+
+ if (Integer.compareUnsigned(end, length) > 0 || Integer.compareUnsigned(start, end) > 0)
+ ThrowHelper.throwArgumentOutOfRangeException(ExceptionArgument.length);
+
+ return new OffsetLength(start, end - start);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof Range) {
+ Range r = (Range) obj;
+ return this.start.equals(r.start) && this.end.equals(r.end);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int h1 = this.start.hashCode();
+ int h2 = this.end.hashCode();
+ return (h1 << 5) + h1 ^ h2;
+ }
+
+ @Override
+ @SuppressWarnings("MethodDoesntCallSuperMethod")
+ protected Range clone() {
+ return new Range(this.start, this.end);
+ }
+
+ @Override
+ public String toString() {
+ return this.start.toString() + ".." + this.end.toString();
+ }
+
+ public static final class OffsetLength {
+ public final int Offset;
+ public final int Length;
+
+ public OffsetLength(int offset, int length) {
+ this.Offset = offset;
+ this.Length = length;
+ }
+ }
+}
diff --git a/src/main/java/com/bestvike/collections/generic/Comparer.java b/src/main/java/com/bestvike/collections/generic/Comparer.java
index 32614731..257dd1e1 100644
--- a/src/main/java/com/bestvike/collections/generic/Comparer.java
+++ b/src/main/java/com/bestvike/collections/generic/Comparer.java
@@ -14,6 +14,8 @@
public final class Comparer implements Comparator {
private static final Comparer> DEFAULT = new Comparer<>(null);
private static final Comparer> DEFAULT_INVARIANT = new Comparer<>(CultureInfo.getInvariantCulture());
+ private static final Comparator FLOAT = new FloatComparer();
+ private static final Comparator DOUBLE = new DoubleComparer();
private final Collator collator;
@@ -31,6 +33,14 @@ public static Comparator DefaultInvariant() {
return (Comparator) DEFAULT_INVARIANT;
}
+ public static Comparator Float() {
+ return FLOAT;
+ }
+
+ public static Comparator Double() {
+ return DOUBLE;
+ }
+
public static Comparator create(Collator collator) {
if (collator == null)
ThrowHelper.throwArgumentNullException(ExceptionArgument.collator);
@@ -62,4 +72,64 @@ public int compare(T x, T y) {
ThrowHelper.throwImplementComparableException();
return 0;
}
+
+
+ private static final class FloatComparer implements Comparator {
+ @Override
+ public int compare(Float x, Float y) {
+ if (x != null) {
+ if (y != null)
+ return this.comparePrimitive(x, y);
+ return 1;
+ }
+ if (y != null)
+ return -1;
+ return 0;
+ }
+
+ private int comparePrimitive(float x, float y) {
+ if (x < y)
+ return -1;
+ if (x > y)
+ return 1;
+ if (x == y)
+ return 0;
+
+ // At least one of the values is NaN.
+ if (Float.isNaN(x))
+ return Float.isNaN(y) ? 0 : -1;
+ else // y is NaN.
+ return 1;
+ }
+ }
+
+
+ private static final class DoubleComparer implements Comparator {
+ @Override
+ public int compare(Double x, Double y) {
+ if (x != null) {
+ if (y != null)
+ return this.comparePrimitive(x, y);
+ return 1;
+ }
+ if (y != null)
+ return -1;
+ return 0;
+ }
+
+ private int comparePrimitive(double x, double y) {
+ if (x < y)
+ return -1;
+ if (x > y)
+ return 1;
+ if (x == y)
+ return 0;
+
+ // At least one of the values is NaN.
+ if (Double.isNaN(x))
+ return Double.isNaN(y) ? 0 : -1;
+ else // y is NaN.
+ return 1;
+ }
+ }
}
diff --git a/src/main/java/com/bestvike/collections/generic/Queue.java b/src/main/java/com/bestvike/collections/generic/Queue.java
new file mode 100644
index 00000000..43769e84
--- /dev/null
+++ b/src/main/java/com/bestvike/collections/generic/Queue.java
@@ -0,0 +1,78 @@
+package com.bestvike.collections.generic;
+
+import com.bestvike.linq.exception.InvalidOperationException;
+import com.bestvike.linq.resources.SR;
+import com.bestvike.out;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Created by 许崇雷 on 2023-10-10.
+ */
+public final class Queue {
+ private final List list;
+
+ public Queue() {
+ this.list = new ArrayList<>();
+ }
+
+ public Queue(int capacity) {
+ this.list = new ArrayList<>(capacity);
+ }
+
+ public int size() {
+ return this.list.size();
+ }
+
+ public boolean isEmpty() {
+ return this.list.isEmpty();
+ }
+
+ public void clear() {
+ this.list.clear();
+ }
+
+ public void enqueue(T item) {
+ this.list.add(item);
+ }
+
+ public T dequeue() {
+ if (this.list.isEmpty())
+ this.throwForEmptyQueue();
+ return this.list.remove(0);
+ }
+
+ public boolean tryDequeue(out result) {
+ if (this.list.isEmpty()) {
+ result.value = null;
+ return false;
+ }
+ result.value = this.list.remove(0);
+ return true;
+ }
+
+ public T peek() {
+ if (this.list.isEmpty())
+ this.throwForEmptyQueue();
+ return this.list.get(0);
+ }
+
+ public boolean tryPeek(out result) {
+ if (this.list.isEmpty()) {
+ result.value = null;
+ return false;
+ }
+ result.value = this.list.get(0);
+ return true;
+ }
+
+ public boolean contains(T item) {
+ return this.list.contains(item);
+ }
+
+ private void throwForEmptyQueue() {
+ assert this.list.isEmpty();
+ throw new InvalidOperationException(SR.InvalidOperation_EmptyQueue);
+ }
+}
diff --git a/src/main/java/com/bestvike/linq/IEnumerable.java b/src/main/java/com/bestvike/linq/IEnumerable.java
index aad54ac7..2ee01a59 100644
--- a/src/main/java/com/bestvike/linq/IEnumerable.java
+++ b/src/main/java/com/bestvike/linq/IEnumerable.java
@@ -1,5 +1,7 @@
package com.bestvike.linq;
+import com.bestvike.Index;
+import com.bestvike.Range;
import com.bestvike.collections.generic.Array;
import com.bestvike.collections.generic.IEqualityComparer;
import com.bestvike.function.Action2;
@@ -23,6 +25,7 @@
import com.bestvike.linq.enumerable.AppendPrepend;
import com.bestvike.linq.enumerable.Average;
import com.bestvike.linq.enumerable.Cast;
+import com.bestvike.linq.enumerable.Chunk;
import com.bestvike.linq.enumerable.Concat;
import com.bestvike.linq.enumerable.Contains;
import com.bestvike.linq.enumerable.Count;
@@ -237,6 +240,10 @@ default IEnumerable cast(Class clazz) {
return Cast.cast(this, clazz);
}
+ default IEnumerable chunk(int size, Class clazz) {
+ return Chunk.chunk(this, size, clazz);
+ }
+
default IEnumerable concat(IEnumerable extends TSource> second) {
return Concat.concat(this, (IEnumerable) second);
}
@@ -289,10 +296,18 @@ default TSource elementAt(int index) {
return ElementAt.elementAt(this, index);
}
+ default TSource elementAt(Index index) {
+ return ElementAt.elementAt(this, index);
+ }
+
default TSource elementAtOrDefault(int index) {
return ElementAt.elementAtOrDefault(this, index);
}
+ default TSource elementAtOrDefault(Index index) {
+ return ElementAt.elementAtOrDefault(this, index);
+ }
+
default IEnumerable except(IEnumerable extends TSource> second) {
return Except.except(this, (IEnumerable) second);
}
@@ -301,12 +316,12 @@ default IEnumerable except(IEnumerable extends TSource> second, IEqua
return Except.except(this, (IEnumerable) second, (IEqualityComparer) comparer);
}
- default IEnumerable exceptBy(IEnumerable extends TSource> second, Func1 super TSource, ? extends TKey> keySelector) {
- return ExceptBy.exceptBy(this, (IEnumerable) second, (Func1) keySelector);
+ default IEnumerable exceptBy(IEnumerable extends TKey> second, Func1 super TSource, ? extends TKey> keySelector) {
+ return ExceptBy.exceptBy(this, (IEnumerable) second, (Func1) keySelector);
}
- default IEnumerable exceptBy(IEnumerable extends TSource> second, Func1 super TSource, ? extends TKey> keySelector, IEqualityComparer super TKey> comparer) {
- return ExceptBy.exceptBy(this, (IEnumerable) second, (Func1) keySelector, (IEqualityComparer) comparer);
+ default IEnumerable exceptBy(IEnumerable extends TKey> second, Func1 super TSource, ? extends TKey> keySelector, IEqualityComparer super TKey> comparer) {
+ return ExceptBy.exceptBy(this, (IEnumerable) second, (Func1) keySelector, (IEqualityComparer) comparer);
}
default int findIndex(Predicate1 super TSource> predicate) {
@@ -329,10 +344,18 @@ default TSource firstOrDefault() {
return First.firstOrDefault(this);
}
+ default TSource firstOrDefault(TSource defaultValue) {
+ return First.firstOrDefault(this, defaultValue);
+ }
+
default TSource firstOrDefault(Predicate1 super TSource> predicate) {
return First.firstOrDefault(this, (Predicate1) predicate);
}
+ default TSource firstOrDefault(Predicate1 super TSource> predicate, TSource defaultValue) {
+ return First.firstOrDefault(this, (Predicate1) predicate, defaultValue);
+ }
+
default String format() {
return Format.format(this);
}
@@ -413,12 +436,12 @@ default IEnumerable intersect(IEnumerable extends TSource> second, IE
return Intersect.intersect(this, (IEnumerable) second, (IEqualityComparer) comparer);
}
- default IEnumerable intersectBy(IEnumerable extends TSource> second, Func1 super TSource, ? extends TKey> keySelector) {
- return IntersectBy.intersectBy(this, (IEnumerable) second, (Func1) keySelector);
+ default IEnumerable intersectBy(IEnumerable extends TKey> second, Func1 super TSource, ? extends TKey> keySelector) {
+ return IntersectBy.intersectBy(this, (IEnumerable) second, (Func1) keySelector);
}
- default IEnumerable intersectBy(IEnumerable extends TSource> second, Func1 super TSource, ? extends TKey> keySelector, IEqualityComparer super TKey> comparer) {
- return IntersectBy.intersectBy(this, (IEnumerable) second, (Func1) keySelector, (IEqualityComparer) comparer);
+ default IEnumerable intersectBy(IEnumerable extends TKey> second, Func1 super TSource, ? extends TKey> keySelector, IEqualityComparer super TKey> comparer) {
+ return IntersectBy.intersectBy(this, (IEnumerable) second, (Func1) keySelector, (IEqualityComparer) comparer);
}
default IEnumerable join(IEnumerable extends TInner> inner, Func1 super TSource, ? extends TKey> outerKeySelector, Func1 super TInner, ? extends TKey> innerKeySelector, Func2 super TSource, ? super TInner, ? extends TResult> resultSelector) {
@@ -461,10 +484,18 @@ default TSource lastOrDefault() {
return Last.lastOrDefault(this);
}
+ default TSource lastOrDefault(TSource defaultValue) {
+ return Last.lastOrDefault(this, defaultValue);
+ }
+
default TSource lastOrDefault(Predicate1 super TSource> predicate) {
return Last.lastOrDefault(this, (Predicate1) predicate);
}
+ default TSource lastOrDefault(Predicate1 super TSource> predicate, TSource defaultValue) {
+ return Last.lastOrDefault(this, (Predicate1) predicate, defaultValue);
+ }
+
default IEnumerable leftJoin(IEnumerable extends TInner> inner, Func1 super TSource, ? extends TKey> outerKeySelector, Func1 super TInner, ? extends TKey> innerKeySelector, Func2 super TSource, ? super TInner, ? extends TResult> resultSelector) {
return Join.leftJoin(this, (IEnumerable) inner, (Func1) outerKeySelector, (Func1) innerKeySelector, (Func2) resultSelector);
}
@@ -533,10 +564,18 @@ default TSource max() {
return Max.max(this);
}
+ default TSource max(Comparator comparer) {
+ return Max.max(this, comparer);
+ }
+
default TSource maxNull() {
return Max.maxNull(this);
}
+ default TSource maxNull(Comparator comparer) {
+ return Max.maxNull(this, comparer);
+ }
+
default int maxInt(IntFunc1 super TSource> selector) {
return Max.maxInt(this, (IntFunc1) selector);
}
@@ -581,10 +620,18 @@ default TResult max(Func1 super TSource, ? extends TResult> selector
return Max.max(this, (Func1) selector);
}
+ default TResult max(Func1 super TSource, ? extends TResult> selector, Comparator comparer) {
+ return Max.max(this, (Func1) selector, comparer);
+ }
+
default TResult maxNull(Func1 super TSource, ? extends TResult> selector) {
return Max.maxNull(this, (Func1) selector);
}
+ default TResult maxNull(Func1 super TSource, ? extends TResult> selector, Comparator comparer) {
+ return Max.maxNull(this, (Func1) selector, comparer);
+ }
+
default TSource maxByInt(IntFunc1 super TSource> keySelector) {
return MaxBy.maxByInt(this, (IntFunc1) keySelector);
}
@@ -629,10 +676,18 @@ default TSource maxBy(Func1 super TSource, ? extends TKey> keySelector)
return MaxBy.maxBy(this, (Func1) keySelector);
}
+ default TSource maxBy(Func1 super TSource, ? extends TKey> keySelector, Comparator comparer) {
+ return MaxBy.maxBy(this, (Func1) keySelector, comparer);
+ }
+
default TSource maxByNull(Func1 super TSource, ? extends TKey> keySelector) {
return MaxBy.maxByNull(this, (Func1) keySelector);
}
+ default TSource maxByNull(Func1 super TSource, ? extends TKey> keySelector, Comparator comparer) {
+ return MaxBy.maxByNull(this, (Func1) keySelector, comparer);
+ }
+
default int minInt() {
return Min.minInt((IEnumerable) this);
}
@@ -677,10 +732,18 @@ default TSource min() {
return Min.min(this);
}
+ default TSource min(Comparator comparer) {
+ return Min.min(this, comparer);
+ }
+
default TSource minNull() {
return Min.minNull(this);
}
+ default TSource minNull(Comparator comparer) {
+ return Min.minNull(this, comparer);
+ }
+
default int minInt(IntFunc1 super TSource> selector) {
return Min.minInt(this, (IntFunc1) selector);
}
@@ -725,10 +788,18 @@ default TResult min(Func1 super TSource, ? extends TResult> selector
return Min.min(this, (Func1) selector);
}
+ default TResult min(Func1 super TSource, ? extends TResult> selector, Comparator comparer) {
+ return Min.min(this, (Func1) selector, comparer);
+ }
+
default TResult minNull(Func1 super TSource, ? extends TResult> selector) {
return Min.minNull(this, (Func1) selector);
}
+ default TResult minNull(Func1 super TSource, ? extends TResult> selector, Comparator comparer) {
+ return Min.minNull(this, (Func1) selector, comparer);
+ }
+
default TSource minByInt(IntFunc1 super TSource> keySelector) {
return MinBy.minByInt(this, (IntFunc1) keySelector);
}
@@ -773,10 +844,18 @@ default TSource minBy(Func1 super TSource, ? extends TKey> keySelector)
return MinBy.minBy(this, (Func1) keySelector);
}
+ default TSource minBy(Func1 super TSource, ? extends TKey> keySelector, Comparator comparer) {
+ return MinBy.minBy(this, (Func1) keySelector, comparer);
+ }
+
default TSource minByNull(Func1 super TSource, ? extends TKey> keySelector) {
return MinBy.minByNull(this, (Func1) keySelector);
}
+ default TSource minByNull(Func1 super TSource, ? extends TKey> keySelector, Comparator comparer) {
+ return MinBy.minByNull(this, (Func1) keySelector, comparer);
+ }
+
default IEnumerable ofType(Class clazz) {
return Cast.ofType(this, clazz);
}
@@ -877,10 +956,18 @@ default TSource singleOrDefault() {
return Single.singleOrDefault(this);
}
+ default TSource singleOrDefault(TSource defaultValue) {
+ return Single.singleOrDefault(this, defaultValue);
+ }
+
default TSource singleOrDefault(Predicate1 super TSource> predicate) {
return Single.singleOrDefault(this, (Predicate1) predicate);
}
+ default TSource singleOrDefault(Predicate1 super TSource> predicate, TSource defaultValue) {
+ return Single.singleOrDefault(this, (Predicate1) predicate, defaultValue);
+ }
+
default IEnumerable skip(int count) {
return Skip.skip(this, count);
}
@@ -981,6 +1068,10 @@ default IEnumerable take(int count) {
return Take.take(this, count);
}
+ default IEnumerable take(Range range) {
+ return Take.take(this, range);
+ }
+
default IEnumerable takeLast(int count) {
return Take.takeLast(this, count);
}
@@ -1026,6 +1117,10 @@ default Map toLinkedMap(Func1 super TSource,
return ToCollection.toLinkedMap(this, (Func1) keySelector, (Func1) elementSelector);
}
+ default Map toLinkedMap(Func1 super TSource, ? extends TKey> keySelector, Func1 super TSource, ? extends TElement> elementSelector, Func2 super TElement,? super TElement,? extends TElement> resultElementSelector) {
+ return ToCollection.toLinkedMap(this, (Func1) keySelector, (Func1) elementSelector, (Func2< TElement, TElement, TElement>) resultElementSelector);
+ }
+
default Set toLinkedSet() {
return ToCollection.toLinkedSet(this);
}
@@ -1058,6 +1153,10 @@ default Map toMap(Func1 super TSource, ? exte
return ToCollection.toMap(this, (Func1) keySelector, (Func1) elementSelector);
}
+ default Map toMap(Func1 super TSource, ? extends TKey> keySelector, Func1 super TSource, ? extends TElement> elementSelector, Func2 super TElement,? super TElement,? extends TElement> resultElementSelector) {
+ return ToCollection.toMap(this, (Func1) keySelector, (Func1) elementSelector, (Func2< TElement, TElement, TElement>) resultElementSelector);
+ }
+
default Set toSet() {
return ToCollection.toSet(this);
}
diff --git a/src/main/java/com/bestvike/linq/enumerable/Chunk.java b/src/main/java/com/bestvike/linq/enumerable/Chunk.java
new file mode 100644
index 00000000..ddf60a3f
--- /dev/null
+++ b/src/main/java/com/bestvike/linq/enumerable/Chunk.java
@@ -0,0 +1,85 @@
+package com.bestvike.linq.enumerable;
+
+import com.bestvike.linq.IEnumerable;
+import com.bestvike.linq.IEnumerator;
+import com.bestvike.linq.exception.ExceptionArgument;
+import com.bestvike.linq.exception.ThrowHelper;
+import com.bestvike.linq.util.ArrayUtils;
+
+import java.util.Arrays;
+
+/**
+ * Created by 许崇雷 on 2023-05-31.
+ */
+public final class Chunk {
+ private Chunk() {
+ }
+
+ public static IEnumerable chunk(IEnumerable source, int size, Class clazz) {
+ if (source == null)
+ ThrowHelper.throwArgumentNullException(ExceptionArgument.source);
+ if (size < 1)
+ ThrowHelper.throwArgumentOutOfRangeException(ExceptionArgument.size);
+ if (clazz == null)
+ ThrowHelper.throwArgumentNullException(ExceptionArgument.clazz);
+
+ return new ChunkIterator<>(source, size, clazz);
+ }
+}
+
+
+final class ChunkIterator extends AbstractIterator {
+ private final IEnumerable source;
+ private final int size;
+ private final Class clazz;
+ private IEnumerator enumerator;
+
+ ChunkIterator(IEnumerable source, int size, Class clazz) {
+ this.source = source;
+ this.size = size;
+ this.clazz = clazz;
+ }
+
+ @Override
+ public AbstractIterator clone() {
+ return new ChunkIterator<>(this.source, this.size, this.clazz);
+ }
+
+ @Override
+ public boolean moveNext() {
+ switch (this.state) {
+ case 1:
+ this.enumerator = this.source.enumerator();
+ this.state = 2;
+ case 2:
+ if (!this.enumerator.moveNext()) {
+ this.close();
+ return false;
+ }
+ TSource[] chunk = ArrayUtils.newInstance(this.clazz, this.size);
+ chunk[0] = this.enumerator.current();
+ int chunkSize = 1;
+ while (chunkSize < chunk.length && this.enumerator.moveNext()) {
+ chunk[chunkSize] = this.enumerator.current();
+ chunkSize++;
+ }
+ if (chunkSize == chunk.length) {
+ this.current = chunk;
+ return true;
+ }
+ this.current = Arrays.copyOf(chunk, chunkSize);
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public void close() {
+ if (this.enumerator != null) {
+ this.enumerator.close();
+ this.enumerator = null;
+ }
+ super.close();
+ }
+}
diff --git a/src/main/java/com/bestvike/linq/enumerable/Concat.java b/src/main/java/com/bestvike/linq/enumerable/Concat.java
index 9d9b042c..aeddd42f 100644
--- a/src/main/java/com/bestvike/linq/enumerable/Concat.java
+++ b/src/main/java/com/bestvike/linq/enumerable/Concat.java
@@ -149,13 +149,13 @@ public IEnumerable getEnumerable(int index) {
public int _getCount(boolean onlyIfCheap) {
out firstCountRef = out.init();
out secondCountRef = out.init();
- if (!EnumerableHelpers.tryGetCount(this.first, firstCountRef)) {
+ if (!Count.tryGetNonEnumeratedCount(this.first, firstCountRef)) {
if (onlyIfCheap)
return -1;
firstCountRef.value = this.first.count();
}
- if (!EnumerableHelpers.tryGetCount(this.second, secondCountRef)) {
+ if (!Count.tryGetNonEnumeratedCount(this.second, secondCountRef)) {
if (onlyIfCheap)
return -1;
secondCountRef.value = this.second.count();
diff --git a/src/main/java/com/bestvike/linq/enumerable/Count.java b/src/main/java/com/bestvike/linq/enumerable/Count.java
index a252a1f9..7ce4ba32 100644
--- a/src/main/java/com/bestvike/linq/enumerable/Count.java
+++ b/src/main/java/com/bestvike/linq/enumerable/Count.java
@@ -6,6 +6,7 @@
import com.bestvike.linq.IEnumerator;
import com.bestvike.linq.exception.ExceptionArgument;
import com.bestvike.linq.exception.ThrowHelper;
+import com.bestvike.out;
/**
* Created by 许崇雷 on 2018-04-27.
@@ -83,4 +84,27 @@ public static long longCount(IEnumerable source, Predicate1 boolean tryGetNonEnumeratedCount(IEnumerable source, out count) {
+ if (source == null)
+ ThrowHelper.throwArgumentNullException(ExceptionArgument.source);
+
+ if (source instanceof ICollection) {
+ ICollection collection = (ICollection) source;
+ count.value = collection._getCount();
+ return true;
+ }
+
+ if (source instanceof IIListProvider) {
+ IIListProvider provider = (IIListProvider) source;
+ int c = provider._getCount(true);
+ if (c >= 0) {
+ count.value = c;
+ return true;
+ }
+ }
+
+ count.value = 0;
+ return false;
+ }
}
diff --git a/src/main/java/com/bestvike/linq/enumerable/Distinct.java b/src/main/java/com/bestvike/linq/enumerable/Distinct.java
index 767c7255..fa133d91 100644
--- a/src/main/java/com/bestvike/linq/enumerable/Distinct.java
+++ b/src/main/java/com/bestvike/linq/enumerable/Distinct.java
@@ -106,8 +106,6 @@ public int _getCount(boolean onlyIfCheap) {
}
private Set fillSet() {
- Set set = new Set<>(this.comparer);
- set.unionWith(this.source);
- return set;
+ return new Set<>(this.source, this.comparer);
}
}
diff --git a/src/main/java/com/bestvike/linq/enumerable/DistinctBy.java b/src/main/java/com/bestvike/linq/enumerable/DistinctBy.java
index d6a3a45c..e441f913 100644
--- a/src/main/java/com/bestvike/linq/enumerable/DistinctBy.java
+++ b/src/main/java/com/bestvike/linq/enumerable/DistinctBy.java
@@ -7,9 +7,6 @@
import com.bestvike.linq.exception.ExceptionArgument;
import com.bestvike.linq.exception.ThrowHelper;
-import java.util.ArrayList;
-import java.util.List;
-
/**
* Created by 许崇雷 on 2018-05-09.
*/
@@ -32,7 +29,7 @@ public static IEnumerable distinctBy(IEnumerable extends Iterator implements IIListProvider {
+final class DistinctByIterator extends AbstractIterator {
private final IEnumerable source;
private final Func1 keySelector;
private final IEqualityComparer comparer;
@@ -46,12 +43,13 @@ final class DistinctByIterator extends Iterator implemen
}
@Override
- public Iterator clone() {
+ public AbstractIterator clone() {
return new DistinctByIterator<>(this.source, this.keySelector, this.comparer);
}
@Override
public boolean moveNext() {
+ TSource element;
switch (this.state) {
case 1:
this.enumerator = this.source.enumerator();
@@ -59,7 +57,7 @@ public boolean moveNext() {
this.close();
return false;
}
- TSource element = this.enumerator.current();
+ element = this.enumerator.current();
this.set = new Set<>(this.comparer);
this.set.add(this.keySelector.apply(element));
this.current = element;
@@ -89,60 +87,4 @@ public void close() {
}
super.close();
}
-
- @Override
- public TSource[] _toArray(Class clazz) {
- LargeArrayBuilder builder = new LargeArrayBuilder<>();
- Set set = new Set<>(this.comparer);
- try (IEnumerator e = this.source.enumerator()) {
- while (e.moveNext()) {
- TSource element = e.current();
- if (set.add(this.keySelector.apply(element)))
- builder.add(element);
- }
- }
-
- return builder.toArray(clazz);
- }
-
- @Override
- public Object[] _toArray() {
- LargeArrayBuilder builder = new LargeArrayBuilder<>();
- Set set = new Set<>(this.comparer);
- try (IEnumerator e = this.source.enumerator()) {
- while (e.moveNext()) {
- TSource element = e.current();
- if (set.add(this.keySelector.apply(element)))
- builder.add(element);
- }
- }
-
- return builder.toArray();
- }
-
- @Override
- public List _toList() {
- List list = new ArrayList<>();
- Set set = new Set<>(this.comparer);
- try (IEnumerator e = this.source.enumerator()) {
- while (e.moveNext()) {
- TSource element = e.current();
- if (set.add(this.keySelector.apply(element)))
- list.add(element);
- }
- }
-
- return list;
- }
-
- @Override
- public int _getCount(boolean onlyIfCheap) {
- return onlyIfCheap ? -1 : this.fillSet().getCount();
- }
-
- private Set fillSet() {
- Set set = new Set<>(this.comparer);
- set.unionWith(this.source, this.keySelector);
- return set;
- }
}
diff --git a/src/main/java/com/bestvike/linq/enumerable/ElementAt.java b/src/main/java/com/bestvike/linq/enumerable/ElementAt.java
index 7d03dca2..72bad16f 100644
--- a/src/main/java/com/bestvike/linq/enumerable/ElementAt.java
+++ b/src/main/java/com/bestvike/linq/enumerable/ElementAt.java
@@ -1,6 +1,8 @@
package com.bestvike.linq.enumerable;
+import com.bestvike.Index;
import com.bestvike.collections.generic.IList;
+import com.bestvike.collections.generic.Queue;
import com.bestvike.linq.IEnumerable;
import com.bestvike.linq.IEnumerator;
import com.bestvike.linq.exception.ExceptionArgument;
@@ -24,26 +26,36 @@ public static TSource elementAt(IEnumerable source, int index
TSource element = partition._tryGetElementAt(index, foundRef);
if (foundRef.value)
return element;
+ } else if (source instanceof IList) {
+ IList list = (IList) source;
+ return list.get(index);
} else {
- if (source instanceof IList) {
- IList list = (IList) source;
- return list.get(index);
- }
-
- if (index >= 0) {
- try (IEnumerator e = source.enumerator()) {
- while (e.moveNext()) {
- if (index == 0)
- return e.current();
- index--;
- }
- }
- }
+ out elementRef = out.init();
+ if (tryGetElement(source, index, elementRef))
+ return elementRef.value;
}
ThrowHelper.throwArgumentOutOfRangeException(ExceptionArgument.index);
return null;
}
+ public static TSource elementAt(IEnumerable source, Index index) {
+ if (source == null)
+ ThrowHelper.throwArgumentNullException(ExceptionArgument.source);
+
+ if (!index.isFromEnd())
+ return elementAt(source, index.getValue());
+
+ out countRef = out.init();
+ if (Count.tryGetNonEnumeratedCount(source, countRef))
+ return elementAt(source, countRef.value - index.getValue());
+
+ out elementRef = out.init();
+ if (!tryGetElementFromEnd(source, index.getValue(), elementRef))
+ ThrowHelper.throwArgumentOutOfRangeException(ExceptionArgument.index);
+
+ return elementRef.value;
+ }
+
public static TSource elementAtOrDefault(IEnumerable source, int index) {
if (source == null)
ThrowHelper.throwArgumentNullException(ExceptionArgument.source);
@@ -52,23 +64,73 @@ public static TSource elementAtOrDefault(IEnumerable source,
IPartition partition = (IPartition) source;
out foundRef = out.init();
return partition._tryGetElementAt(index, foundRef);
+ } else if (source instanceof IList) {
+ IList list = (IList) source;
+ return index >= 0 && index < list._getCount() ? list.get(index) : null;
}
+ out elementRef = out.init();
+ tryGetElement(source, index, elementRef);
+ return elementRef.value;
+ }
+
+ public static TSource elementAtOrDefault(IEnumerable source, Index index) {
+ if (source == null)
+ ThrowHelper.throwArgumentNullException(ExceptionArgument.source);
+
+ if (!index.isFromEnd())
+ return elementAtOrDefault(source, index.getValue());
+
+ out countRef = out.init();
+ if (Count.tryGetNonEnumeratedCount(source, countRef))
+ return elementAtOrDefault(source, countRef.value - index.getValue());
+
+ out elementRef = out.init();
+ tryGetElementFromEnd(source, index.getValue(), elementRef);
+ return elementRef.value;
+ }
+
+ private static boolean tryGetElement(IEnumerable source, int index, out element) {
+ assert source != null;
+
if (index >= 0) {
- if (source instanceof IList) {
- IList list = (IList) source;
- if (index < list._getCount())
- return list.get(index);
- } else {
- try (IEnumerator e = source.enumerator()) {
+ try (IEnumerator e = source.enumerator()) {
+ while (e.moveNext()) {
+ if (index == 0) {
+ element.value = e.current();
+ return true;
+ }
+ index--;
+ }
+ }
+ }
+ element.value = null;
+ return false;
+ }
+
+ private static boolean tryGetElementFromEnd(IEnumerable source, int indexFromEnd, out element) {
+ assert source != null;
+ if (indexFromEnd > 0) {
+ try (IEnumerator e = source.enumerator()) {
+ if (e.moveNext()) {
+ Queue queue = new Queue<>();
+ queue.enqueue(e.current());
while (e.moveNext()) {
- if (index == 0)
- return e.current();
- index--;
+ if (queue.size() == indexFromEnd) {
+ queue.dequeue();
+ }
+ queue.enqueue(e.current());
+ }
+
+ if (queue.size() == indexFromEnd) {
+ element.value = queue.dequeue();
+ return true;
}
}
}
}
- return null;
+
+ element.value = null;
+ return false;
}
}
diff --git a/src/main/java/com/bestvike/linq/enumerable/Except.java b/src/main/java/com/bestvike/linq/enumerable/Except.java
index 7d2d71be..8535e013 100644
--- a/src/main/java/com/bestvike/linq/enumerable/Except.java
+++ b/src/main/java/com/bestvike/linq/enumerable/Except.java
@@ -50,8 +50,7 @@ public AbstractIterator