This is an automated email from the ASF dual-hosted git repository.
jhyde pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git
The following commit(s) were added to refs/heads/main by this push:
new 95408cc71f [CALCITE-6050] Add interface ImmutablePairList
95408cc71f is described below
commit 95408cc71fb7cc2742d09dac1a2fe113daf5328f
Author: Julian Hyde <[email protected]>
AuthorDate: Sat Sep 23 18:33:34 2023 -0700
[CALCITE-6050] Add interface ImmutablePairList
In [CALCITE-5706], we added class PairList; this change adds
interface ImmutablePairList, which is optimized for cases
where the list does not change after creation. The
implementation stores entries in an array, rather than a
list, saving memory allocations and indirection.
PairList and ImmutablePairList are now interfaces; this
allows us to have multiple implementations that do not have
a common base class.
Optimize representation of ImmutablePairList. There are
dedicated classes for size = 0 and 1; and we use a backing
array if size is 2 or more.
In Pair, add method forEachIndexed().
In PairList, add methods left(int), right(int), transform2(),
addAll(). Override List.subList(), and add reverse() and
reversed().
Refactor RelBuilder to use PairList, and obsolete class Field.
Use PairList in RelRoot and Schemas.path.
---
.../org/apache/calcite/jdbc/CalciteSchema.java | 3 +-
.../apache/calcite/prepare/CalcitePrepareImpl.java | 2 +-
.../org/apache/calcite/rel/AbstractRelNode.java | 2 +-
.../main/java/org/apache/calcite/rel/RelRoot.java | 36 +-
.../apache/calcite/runtime/ImmutablePairList.java | 114 ++++
.../java/org/apache/calcite/runtime/PairList.java | 259 +++-----
.../java/org/apache/calcite/runtime/PairLists.java | 724 +++++++++++++++++++++
.../java/org/apache/calcite/schema/Schemas.java | 34 +-
.../java/org/apache/calcite/tools/RelBuilder.java | 148 +++--
.../org/apache/calcite/util/BuiltInMethod.java | 5 +-
.../main/java/org/apache/calcite/util/Pair.java | 51 ++
.../java/org/apache/calcite/util/PairListTest.java | 161 ++++-
.../calcite/test/catalog/MockCatalogReader.java | 10 +-
13 files changed, 1245 insertions(+), 304 deletions(-)
diff --git a/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java
b/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java
index 236b4989d8..4466bd3cd5 100644
--- a/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java
+++ b/core/src/main/java/org/apache/calcite/jdbc/CalciteSchema.java
@@ -39,7 +39,6 @@ import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.ImmutableSortedSet;
-import com.google.common.collect.Lists;
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -247,7 +246,7 @@ public abstract class CalciteSchema {
list.add(s.name);
}
}
- return ImmutableList.copyOf(Lists.reverse(list));
+ return ImmutableList.copyOf(list).reverse();
}
public final @Nullable CalciteSchema getSubSchema(String schemaName,
diff --git
a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
index d78e08cded..4647be8c15 100644
--- a/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
+++ b/core/src/main/java/org/apache/calcite/prepare/CalcitePrepareImpl.java
@@ -1042,7 +1042,7 @@ public class CalcitePrepareImpl implements CalcitePrepare
{
: RelCollations.EMPTY;
RelRoot root =
new RelRoot(rel, resultType, SqlKind.SELECT, fields, collation,
- new ArrayList<>());
+ ImmutableList.of());
if (timingTracer != null) {
timingTracer.traceTime("end sql2rel");
diff --git a/core/src/main/java/org/apache/calcite/rel/AbstractRelNode.java
b/core/src/main/java/org/apache/calcite/rel/AbstractRelNode.java
index 66949853a2..c14f4d2a49 100644
--- a/core/src/main/java/org/apache/calcite/rel/AbstractRelNode.java
+++ b/core/src/main/java/org/apache/calcite/rel/AbstractRelNode.java
@@ -486,7 +486,7 @@ public abstract class AbstractRelNode implements RelNode {
// convert it to String to keep the same behaviour.
value = "" + value;
}
- attrs.add(Pair.of(term, value));
+ attrs.add(term, value);
return this;
}
diff --git a/core/src/main/java/org/apache/calcite/rel/RelRoot.java
b/core/src/main/java/org/apache/calcite/rel/RelRoot.java
index 7e6ba78e56..b7c5e0a1d3 100644
--- a/core/src/main/java/org/apache/calcite/rel/RelRoot.java
+++ b/core/src/main/java/org/apache/calcite/rel/RelRoot.java
@@ -21,6 +21,8 @@ import org.apache.calcite.rel.logical.LogicalProject;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rex.RexBuilder;
import org.apache.calcite.rex.RexNode;
+import org.apache.calcite.runtime.ImmutablePairList;
+import org.apache.calcite.runtime.PairList;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.util.ImmutableIntList;
import org.apache.calcite.util.Pair;
@@ -31,7 +33,9 @@ import com.google.common.collect.ImmutableSet;
import java.util.ArrayList;
import java.util.List;
-import java.util.Objects;
+import java.util.Map;
+
+import static java.util.Objects.requireNonNull;
/**
* Root of a tree of {@link RelNode}.
@@ -78,7 +82,7 @@ public class RelRoot {
public final RelNode rel;
public final RelDataType validatedRowType;
public final SqlKind kind;
- public final ImmutableList<Pair<Integer, String>> fields;
+ public final ImmutablePairList<Integer, String> fields;
public final RelCollation collation;
public final ImmutableList<RelHint> hints;
@@ -90,12 +94,13 @@ public class RelRoot {
*/
public RelRoot(RelNode rel, RelDataType validatedRowType, SqlKind kind,
- List<Pair<Integer, String>> fields, RelCollation collation,
List<RelHint> hints) {
+ Iterable<? extends Map.Entry<Integer, String>> fields,
+ RelCollation collation, List<RelHint> hints) {
this.rel = rel;
this.validatedRowType = validatedRowType;
this.kind = kind;
- this.fields = ImmutableList.copyOf(fields);
- this.collation = Objects.requireNonNull(collation, "collation");
+ this.fields = ImmutablePairList.copyOf(fields);
+ this.collation = requireNonNull(collation, "collation");
this.hints = ImmutableList.copyOf(hints);
}
@@ -106,11 +111,11 @@ public class RelRoot {
/** Creates a simple RelRoot. */
public static RelRoot of(RelNode rel, RelDataType rowType, SqlKind kind) {
- final ImmutableIntList refs =
- ImmutableIntList.identity(rowType.getFieldCount());
- final List<String> names = rowType.getFieldNames();
- return new RelRoot(rel, rowType, kind, Pair.zip(refs, names),
- RelCollations.EMPTY, new ArrayList<>());
+ final PairList<Integer, String> fields = PairList.of();
+ Pair.forEach(ImmutableIntList.identity(rowType.getFieldCount()),
+ rowType.getFieldNames(), fields::add);
+ return new RelRoot(rel, rowType, kind, fields, RelCollations.EMPTY,
+ ImmutableList.of());
}
@Override public String toString() {
@@ -164,15 +169,14 @@ public class RelRoot {
}
final List<RexNode> projects = new ArrayList<>(fields.size());
final RexBuilder rexBuilder = rel.getCluster().getRexBuilder();
- for (Pair<Integer, String> field : fields) {
- projects.add(rexBuilder.makeInputRef(rel, field.left));
- }
- return LogicalProject.create(rel, hints, projects, Pair.right(fields),
ImmutableSet.of());
+ fields.forEach((i, name) -> projects.add(rexBuilder.makeInputRef(rel, i)));
+ return LogicalProject.create(rel, hints, projects, fields.rightList(),
+ ImmutableSet.of());
}
public boolean isNameTrivial() {
final RelDataType inputRowType = rel.getRowType();
- return Pair.right(fields).equals(inputRowType.getFieldNames());
+ return fields.rightList().equals(inputRowType.getFieldNames());
}
public boolean isRefTrivial() {
@@ -183,7 +187,7 @@ public class RelRoot {
return true;
}
final RelDataType inputRowType = rel.getRowType();
- return Mappings.isIdentity(Pair.left(fields),
inputRowType.getFieldCount());
+ return Mappings.isIdentity(fields.leftList(),
inputRowType.getFieldCount());
}
public boolean isCollationTrivial() {
diff --git
a/core/src/main/java/org/apache/calcite/runtime/ImmutablePairList.java
b/core/src/main/java/org/apache/calcite/runtime/ImmutablePairList.java
new file mode 100644
index 0000000000..f285338675
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/runtime/ImmutablePairList.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.runtime;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import static org.apache.calcite.linq4j.Nullness.castNonNull;
+
+import static java.util.Objects.requireNonNull;
+
+/** Immutable list of pairs.
+ *
+ * @param <T> First type
+ * @param <U> Second type
+ */
+public interface ImmutablePairList<T, U> extends PairList<T, U> {
+
+ /** Creates an empty ImmutablePairList. */
+ @SuppressWarnings("unchecked")
+ static <T, U> ImmutablePairList<T, U> of() {
+ return (ImmutablePairList<T, U>) PairLists.EMPTY;
+ }
+
+ /** Creates a singleton ImmutablePairList. */
+ static <T, U> ImmutablePairList<T, U> of(T t, U u) {
+ return new PairLists.SingletonImmutablePairList<>(t, u);
+ }
+
+ /** Creates an ImmutablePairList with one or more entries. */
+ static <T, U> PairList<T, U> copyOf(T t, U u, Object... rest) {
+ checkArgument(rest.length % 2 == 0, "even number");
+ if (rest.length == 0) {
+ return new PairLists.SingletonImmutablePairList<>(t, u);
+ }
+ Object[] elements = new Object[rest.length + 2];
+ elements[0] = requireNonNull(t, "t");
+ elements[1] = requireNonNull(u, "u");
+ System.arraycopy(rest, 0, elements, 2, rest.length);
+ return new PairLists.ArrayImmutablePairList<>(elements);
+ }
+
+ /** Creates an ImmutablePairList whose contents are a copy of a given
+ * collection. */
+ @SuppressWarnings("unchecked")
+ static <@NonNull T, @NonNull U> ImmutablePairList<T, U> copyOf(
+ Iterable<? extends Map.Entry<T, U>> iterable) {
+ // Every PairList - mutable and immutable - knows how to quickly make
+ // itself immutable.
+ if (iterable instanceof PairList) {
+ return ((PairList<T, U>) iterable).immutable();
+ }
+
+ // If it's a collection, we know its size, and therefore can create an
+ // array directly, without an intermediate ArrayList.
+ if (iterable instanceof Collection) {
+ final Collection<? extends Map.Entry<T, U>> collection =
+ (Collection<? extends Map.Entry<T, U>>) iterable;
+ switch (collection.size()) {
+ case 0:
+ return of();
+
+ case 1:
+ // Use of iterator is suboptimal. If we knew this was a list we could
+ // call get(0), but the special case doesn't seem worth the effort.
+ final Map.Entry<T, U> entry = iterable.iterator().next();
+ return of(entry.getKey(), entry.getValue());
+
+ default:
+ Object[] elements = new Object[2 * collection.size()];
+ int i = 0;
+ for (Map.Entry<T, U> entry2 : iterable) {
+ elements[i++] = castNonNull(entry2.getKey());
+ elements[i++] = castNonNull(entry2.getValue());
+ }
+ return new PairLists.ArrayImmutablePairList<>(elements);
+ }
+ }
+
+ // Not a collection, so we don't know its size in advance.
+ final List<Object> list = new ArrayList<>();
+ iterable.forEach(entry -> {
+ list.add(castNonNull(entry.getKey()));
+ list.add(castNonNull(entry.getValue()));
+ });
+ return PairLists.immutableBackedBy(list);
+ }
+
+ @Override default ImmutablePairList<T, U> immutable() {
+ return this;
+ }
+
+ @Override ImmutablePairList<T, U> subList(int fromIndex, int toIndex);
+}
diff --git a/core/src/main/java/org/apache/calcite/runtime/PairList.java
b/core/src/main/java/org/apache/calcite/runtime/PairList.java
index eac9f00602..ef5819ebee 100644
--- a/core/src/main/java/org/apache/calcite/runtime/PairList.java
+++ b/core/src/main/java/org/apache/calcite/runtime/PairList.java
@@ -16,62 +16,53 @@
*/
package org.apache.calcite.runtime;
-import org.apache.calcite.linq4j.function.Functions;
-
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import org.checkerframework.checker.nullness.qual.Nullable;
-import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
-import java.util.RandomAccess;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import static com.google.common.base.Preconditions.checkArgument;
-import static java.util.Objects.requireNonNull;
+import static org.apache.calcite.linq4j.Nullness.castNonNullList;
/** A list of pairs, stored as a quotient list.
*
* @param <T> First type
* @param <U> Second type
*/
-public class PairList<T, U> extends AbstractList<Map.Entry<T, U>> {
- final List<@Nullable Object> list;
-
- private PairList(List<@Nullable Object> list) {
- this.list = list;
- }
-
+public interface PairList<T, U> extends List<Map.Entry<T, U>> {
/** Creates an empty PairList. */
- public static <T, U> PairList<T, U> of() {
- return new PairList<>(new ArrayList<>());
+ static <T, U> PairList<T, U> of() {
+ return new PairLists.MutablePairList<>(new ArrayList<>());
}
/** Creates a singleton PairList. */
@SuppressWarnings("RedundantCast")
- public static <T, U> PairList<T, U> of(T t, U u) {
+ static <T, U> PairList<T, U> of(T t, U u) {
final List<@Nullable Object> list = new ArrayList<>();
list.add((Object) t);
list.add((Object) u);
- return new PairList<>(list);
+ return new PairLists.MutablePairList<>(list);
}
/** Creates a PairList with one or more entries. */
- public static <T, U> PairList<T, U> copyOf(T t, U u, Object... rest) {
+ static <T, U> PairList<T, U> copyOf(T t, U u, Object... rest) {
checkArgument(rest.length % 2 == 0, "even number");
- return new PairList<>(new ArrayList<>(Lists.asList(t, u, rest)));
+ final List<Object> list = Lists.asList(t, u, rest);
+ return new PairLists.MutablePairList<>(new ArrayList<>(list));
}
/** Creates an empty PairList with a specified initial capacity. */
- public static <T, U> PairList<T, U> withCapacity(int initialCapacity) {
+ static <T, U> PairList<T, U> withCapacity(int initialCapacity) {
return backedBy(new ArrayList<>(initialCapacity));
}
@@ -79,226 +70,125 @@ public class PairList<T, U> extends
AbstractList<Map.Entry<T, U>> {
*
* <p>Changes to the backing list will be reflected in the PairList.
* If the backing list is immutable, this PairList will be also. */
- public static <T, U> PairList<T, U> backedBy(List<@Nullable Object> list) {
- return new PairList<>(list);
+ static <T, U> PairList<T, U> backedBy(List<@Nullable Object> list) {
+ return new PairLists.MutablePairList<>(list);
}
/** Creates a PairList from a Map. */
@SuppressWarnings("RedundantCast")
- public static <T, U> PairList<T, U> of(Map<T, U> map) {
+ static <T, U> PairList<T, U> of(Map<T, U> map) {
final List<@Nullable Object> list = new ArrayList<>(map.size() * 2);
map.forEach((t, u) -> {
list.add((Object) t);
list.add((Object) u);
});
- return new PairList<>(list);
+ return new PairLists.MutablePairList<>(list);
}
/** Creates a Builder. */
- public static <T, U> Builder<T, U> builder() {
+ static <T, U> Builder<T, U> builder() {
return new Builder<>();
}
- @SuppressWarnings("unchecked")
- @Override public Map.Entry<T, U> get(int index) {
- int x = index * 2;
- return new MapEntry<>((T) list.get(x), (U) list.get(x + 1));
- }
-
- @Override public int size() {
- return list.size() / 2;
- }
-
- @Override public void clear() {
- list.clear();
- }
-
- @SuppressWarnings("RedundantCast")
- @Override public boolean add(Map.Entry<T, U> entry) {
- list.add((Object) entry.getKey());
- list.add((Object) entry.getValue());
- return true;
- }
-
- @SuppressWarnings("RedundantCast")
- @Override public void add(int index, Map.Entry<T, U> entry) {
- int x = index * 2;
- list.add(x, (Object) entry.getKey());
- list.add(x + 1, (Object) entry.getValue());
- }
-
/** Adds a pair to this list. */
- @SuppressWarnings("RedundantCast")
- public void add(T t, U u) {
- list.add((Object) t);
- list.add((Object) u);
+ default void add(T t, U u) {
+ throw new UnsupportedOperationException("add");
}
/** Adds a pair to this list at a given position. */
- @SuppressWarnings("RedundantCast")
- public void add(int index, T t, U u) {
- int x = index * 2;
- list.add(x, (Object) t);
- list.add(x + 1, (Object) u);
+ default void add(int index, T t, U u) {
+ throw new UnsupportedOperationException("add");
}
/** Adds to this list the contents of another PairList.
*
* <p>Equivalent to {@link #addAll(Collection)}, but more efficient. */
- public boolean addAll(PairList<T, U> list2) {
- return list.addAll(list2.list);
+ default boolean addAll(PairList<T, U> list2) {
+ throw new UnsupportedOperationException("addAll");
}
/** Adds to this list, at a given index, the contents of another PairList.
*
* <p>Equivalent to {@link #addAll(int, Collection)}, but more efficient. */
- public boolean addAll(int index, PairList<T, U> list2) {
- int x = index * 2;
- return list.addAll(x, list2.list);
+ default boolean addAll(int index, PairList<T, U> list2) {
+ throw new UnsupportedOperationException("addAll");
}
- @Override public Map.Entry<T, U> set(int index, Map.Entry<T, U> entry) {
- return set(index, entry.getKey(), entry.getValue());
+ /** Sets the entry at position {@code index} to the pair {@code (t, u)}. */
+ default Map.Entry<T, U> set(int index, T t, U u) {
+ throw new UnsupportedOperationException("set");
}
- @SuppressWarnings("unchecked")
- public Map.Entry<T, U> set(int index, T t, U u) {
- int x = index * 2;
- T t0 = (T) list.set(x, t);
- U u0 = (U) list.set(x + 1, u);
- return new MapEntry<>(t0, u0);
+ @Override default Map.Entry<T, U> remove(int index) {
+ throw new UnsupportedOperationException("remove");
}
- @SuppressWarnings("unchecked")
- @Override public Map.Entry<T, U> remove(int index) {
- final int x = index * 2;
- T t = (T) list.remove(x);
- U u = (U) list.remove(x);
- return new MapEntry<>(t, u);
- }
+ /** Returns the left part of the {@code index}th pair. */
+ T left(int index);
+
+ /** Returns the right part of the {@code index}th pair. */
+ U right(int index);
/** Returns an unmodifiable list view consisting of the left entry of each
* pair. */
- @SuppressWarnings("unchecked")
- public List<T> leftList() {
- final int size = list.size() / 2;
- return new RandomAccessList<T>() {
- @Override public int size() {
- return size;
- }
-
- @Override public T get(int index) {
- return (T) list.get(index * 2);
- }
- };
- }
+ List<T> leftList();
/** Returns an unmodifiable list view consisting of the right entry of each
* pair. */
- @SuppressWarnings("unchecked")
- public List<U> rightList() {
- final int size = list.size() / 2;
- return new RandomAccessList<U>() {
- @Override public int size() {
- return size;
- }
-
- @Override public U get(int index) {
- return (U) list.get(index * 2 + 1);
- }
- };
- }
+ List<U> rightList();
/** Calls a BiConsumer with each pair in this list. */
- @SuppressWarnings("unchecked")
- public void forEach(BiConsumer<T, U> consumer) {
- requireNonNull(consumer, "consumer");
- for (int i = 0; i < list.size();) {
- T t = (T) list.get(i++);
- U u = (U) list.get(i++);
- consumer.accept(t, u);
- }
- }
+ void forEach(BiConsumer<T, U> consumer);
/** Calls a BiConsumer with each pair in this list. */
- @SuppressWarnings("unchecked")
- public void forEachIndexed(IndexedBiConsumer<T, U> consumer) {
- requireNonNull(consumer, "consumer");
- for (int i = 0, j = 0; i < list.size();) {
- T t = (T) list.get(i++);
- U u = (U) list.get(i++);
- consumer.accept(j++, t, u);
- }
- }
+ void forEachIndexed(IndexedBiConsumer<T, U> consumer);
/** Creates an {@link ImmutableMap} whose entries are the pairs in this list.
* Throws if keys are not unique. */
- public ImmutableMap<T, U> toImmutableMap() {
+ default ImmutableMap<T, U> toImmutableMap() {
final ImmutableMap.Builder<T, U> b = ImmutableMap.builder();
forEach((t, u) -> b.put(t, u));
return b.build();
}
- /** Returns an immutable PairList whose contents are the same as this
+ /** Returns an ImmutablePairList whose contents are the same as this
* PairList. */
- public PairList<T, U> immutable() {
- final List<@Nullable Object> immutableList = ImmutableList.copyOf(list);
- return backedBy(immutableList);
- }
+ ImmutablePairList<T, U> immutable();
/** Applies a mapping function to each element of this list. */
- @SuppressWarnings("unchecked")
- public <R> List<R> transform(BiFunction<T, U, R> function) {
- return Functions.generate(list.size() / 2, index -> {
- final int x = index * 2;
- final T t = (T) list.get(x);
- final U u = (U) list.get(x + 1);
- return function.apply(t, u);
- });
- }
+ <R> List<R> transform(BiFunction<T, U, R> function);
+
+ /** Applies a mapping function to each element of this list. */
+ <R> ImmutableList<R> transform2(BiFunction<T, U, R> function);
+
+ @Override PairList<T, U> subList(int fromIndex, int toIndex);
/** Returns whether the predicate is true for at least one pair
* in this list. */
- @SuppressWarnings("unchecked")
- public boolean anyMatch(BiPredicate<T, U> predicate) {
- for (int i = 0; i < list.size();) {
- final T t = (T) list.get(i++);
- final U u = (U) list.get(i++);
- if (predicate.test(t, u)) {
- return true;
- }
- }
- return false;
- }
+ boolean anyMatch(BiPredicate<T, U> predicate);
/** Returns whether the predicate is true for all pairs
* in this list. */
- @SuppressWarnings("unchecked")
- public boolean allMatch(BiPredicate<T, U> predicate) {
- for (int i = 0; i < list.size();) {
- final T t = (T) list.get(i++);
- final U u = (U) list.get(i++);
- if (!predicate.test(t, u)) {
- return false;
- }
- }
- return true;
- }
+ boolean allMatch(BiPredicate<T, U> predicate);
/** Returns whether the predicate is true for no pairs
* in this list. */
- @SuppressWarnings("unchecked")
- public boolean noMatch(BiPredicate<T, U> predicate) {
- for (int i = 0; i < list.size();) {
- final T t = (T) list.get(i++);
- final U u = (U) list.get(i++);
- if (predicate.test(t, u)) {
- return false;
- }
- }
- return true;
+ boolean noMatch(BiPredicate<T, U> predicate);
+
+ /** Reverses the contents of this PairList.
+ * Throws if this PairList is immutable.
+ *
+ * @see #reversed() */
+ default void reverse() {
+ throw new UnsupportedOperationException("reverse");
}
+ /** Returns an immutable copy of this ImmutablePairList with the elements
+ * reversed.
+ *
+ * <p>Throws {@link NullPointerException} if any keys or values are null. */
+ ImmutablePairList<T, U> reversed();
+
/** Action to be taken each step of an indexed iteration over a PairList.
*
* @param <T> First type
@@ -306,7 +196,7 @@ public class PairList<T, U> extends
AbstractList<Map.Entry<T, U>> {
*
* @see PairList#forEachIndexed(IndexedBiConsumer)
*/
- public interface IndexedBiConsumer<T, U> {
+ interface IndexedBiConsumer<T, U> {
/**
* Performs this operation on the given arguments.
*
@@ -321,7 +211,7 @@ public class PairList<T, U> extends
AbstractList<Map.Entry<T, U>> {
*
* @param <T> First type
* @param <U> Second type */
- public static class Builder<T, U> {
+ class Builder<T, U> {
final List<@Nullable Object> list = new ArrayList<>();
/** Adds a pair to the list under construction. */
@@ -334,14 +224,23 @@ public class PairList<T, U> extends
AbstractList<Map.Entry<T, U>> {
/** Builds the PairList. */
public PairList<T, U> build() {
- return new PairList<>(list);
+ return new PairLists.MutablePairList<>(list);
+ }
+
+ /** Builds an ImmutablePairList. */
+ public ImmutablePairList<T, U> buildImmutable() {
+ return PairLists.immutableBackedBy(castNonNullList(list));
}
}
- /** Base class for a list that implements {@link java.util.RandomAccess}.
- *
- * @param <E> Element type */
- private abstract static class RandomAccessList<E>
- extends AbstractList<E> implements RandomAccess {
+ /** For code invoked via Janino, which cannot call static methods
+ * in interfaces. */
+ @Deprecated
+ class Helper {
+ private Helper() {}
+
+ public static <T, U> PairList<T, U> copyOf(T t, U u, Object... rest) {
+ return PairList.copyOf(t, u, rest);
+ }
}
}
diff --git a/core/src/main/java/org/apache/calcite/runtime/PairLists.java
b/core/src/main/java/org/apache/calcite/runtime/PairLists.java
new file mode 100644
index 0000000000..51a7c4a0ed
--- /dev/null
+++ b/core/src/main/java/org/apache/calcite/runtime/PairLists.java
@@ -0,0 +1,724 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.calcite.runtime;
+
+import org.apache.calcite.linq4j.function.Functions;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.errorprone.annotations.CanIgnoreReturnValue;
+
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+
+import java.util.AbstractList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.RandomAccess;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.BiPredicate;
+
+import static org.apache.calcite.linq4j.Nullness.castNonNull;
+import static org.apache.calcite.linq4j.Nullness.castNonNullArray;
+import static org.apache.calcite.linq4j.Nullness.castNonNullList;
+
+import static java.util.Objects.requireNonNull;
+
+/** Various implementations of {@link PairList}. */
+class PairLists {
+ static final ImmutablePairList<Object, Object> EMPTY =
+ new EmptyImmutablePairList<>();
+
+ private PairLists() {
+ }
+
+ @SuppressWarnings("unchecked")
+ static <T, U> ImmutablePairList<T, U> immutableBackedBy(
+ List<Object> list) {
+ switch (list.size()) {
+ case 0:
+ return ImmutablePairList.of();
+ case 2:
+ return new SingletonImmutablePairList<>(
+ castNonNull((T) list.get(0)),
+ castNonNull((U) list.get(1)));
+ default:
+ return new ArrayImmutablePairList<>(list.toArray());
+ }
+ }
+
+ @CanIgnoreReturnValue
+ static @NonNull Object[] checkElementsNotNull(@Nullable Object... elements) {
+ for (int i = 0; i < elements.length; i++) {
+ checkElementNotNull(i, elements[i]);
+ }
+ return castNonNullArray(elements);
+ }
+
+ static void checkElementNotNull(int i, @Nullable Object element) {
+ if (element == null) {
+ throw new NullPointerException((i % 2 == 0 ? "key" : "value")
+ + " at index " + (i / 2));
+ }
+ }
+
+ /** Base class for all implementations of PairList.
+ *
+ * @param <T> First type
+ * @param <U> Second type
+ */
+ abstract static class AbstractPairList<T, U>
+ extends AbstractList<Map.Entry<T, U>>
+ implements PairList<T, U> {
+ /** Returns a list containing the alternating left and right elements
+ * of each pair. */
+ abstract List<@Nullable Object> backingList();
+
+ @Override public abstract PairList<T, U> subList(int fromIndex,
+ int toIndex);
+
+ static void subListRangeCheck(int fromIndex, int toIndex, int size) {
+ if (fromIndex < 0) {
+ throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
+ }
+ if (toIndex > size) {
+ throw new IndexOutOfBoundsException("toIndex = " + toIndex);
+ }
+ if (fromIndex > toIndex) {
+ throw new IllegalArgumentException("fromIndex(" + fromIndex
+ + ") > toIndex(" + toIndex + ")");
+ }
+ }
+ }
+
+ /** Mutable version of {@link PairList}.
+ *
+ * @param <T> First type
+ * @param <U> Second type
+ */
+ static class MutablePairList<T, U> extends AbstractPairList<T, U> {
+ final List<@Nullable Object> list;
+
+ MutablePairList(List<@Nullable Object> list) {
+ this.list = list;
+ }
+
+ @Override List<@Nullable Object> backingList() {
+ return list;
+ }
+
+ @Override public void clear() {
+ list.clear();
+ }
+
+ @Override public int size() {
+ return list.size() / 2;
+ }
+
+ @Override public boolean isEmpty() {
+ return list.isEmpty();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public Map.Entry<T, U> get(int index) {
+ int x = index * 2;
+ return new MapEntry<>((T) list.get(x), (U) list.get(x + 1));
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public T left(int index) {
+ int x = index * 2;
+ return (T) list.get(x);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public U right(int index) {
+ int x = index * 2;
+ return (U) list.get(x + 1);
+ }
+
+ @Override public Map.Entry<T, U> set(int index,
+ Map.@Nullable Entry<T, U> entry) {
+ if (entry == null) {
+ return set(index, castNonNull(null), castNonNull(null));
+ }
+ return set(index, entry.getKey(), entry.getValue());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public Map.Entry<T, U> set(int index, T t, U u) {
+ int x = index * 2;
+ T t0 = (T) list.set(x, t);
+ U u0 = (U) list.set(x + 1, u);
+ return new MapEntry<>(t0, u0);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public Map.Entry<T, U> remove(int index) {
+ final int x = index * 2;
+ T t = (T) list.remove(x);
+ U u = (U) list.remove(x);
+ return new MapEntry<>(t, u);
+ }
+
+ @SuppressWarnings("RedundantCast")
+ @Override public boolean add(Map.Entry<T, U> entry) {
+ list.add((Object) entry.getKey());
+ list.add((Object) entry.getValue());
+ return true;
+ }
+
+ @SuppressWarnings("RedundantCast")
+ @Override public void add(int index, Map.Entry<T, U> entry) {
+ int x = index * 2;
+ list.add(x, (Object) entry.getKey());
+ list.add(x + 1, (Object) entry.getValue());
+ }
+
+ @SuppressWarnings("RedundantCast")
+ @Override public void add(T t, U u) {
+ list.add((Object) t);
+ list.add((Object) u);
+ }
+
+ @SuppressWarnings("RedundantCast")
+ @Override public void add(int index, T t, U u) {
+ int x = index * 2;
+ list.add(x, (Object) t);
+ list.add(x + 1, (Object) u);
+ }
+
+ @Override public boolean addAll(PairList<T, U> list2) {
+ return list.addAll(((AbstractPairList<T, U>) list2).backingList());
+ }
+
+ @Override public boolean addAll(int index, PairList<T, U> list2) {
+ int x = index * 2;
+ return list.addAll(x, ((AbstractPairList<T, U>) list2).backingList());
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public List<T> leftList() {
+ final int size = list.size() / 2;
+ return new RandomAccessList<T>() {
+ @Override public int size() {
+ return size;
+ }
+
+ @Override public T get(int index) {
+ return (T) list.get(index * 2);
+ }
+ };
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public List<U> rightList() {
+ final int size = list.size() / 2;
+ return new RandomAccessList<U>() {
+ @Override public int size() {
+ return size;
+ }
+
+ @Override public U get(int index) {
+ return (U) list.get(index * 2 + 1);
+ }
+ };
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public void forEach(BiConsumer<T, U> consumer) {
+ requireNonNull(consumer, "consumer");
+ for (int i = 0; i < list.size();) {
+ T t = (T) list.get(i++);
+ U u = (U) list.get(i++);
+ consumer.accept(t, u);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public void forEachIndexed(IndexedBiConsumer<T, U> consumer) {
+ requireNonNull(consumer, "consumer");
+ for (int i = 0, j = 0; i < list.size();) {
+ T t = (T) list.get(i++);
+ U u = (U) list.get(i++);
+ consumer.accept(j++, t, u);
+ }
+ }
+
+ @Override public ImmutableMap<T, U> toImmutableMap() {
+ final ImmutableMap.Builder<T, U> b = ImmutableMap.builder();
+ forEach((t, u) -> b.put(t, u));
+ return b.build();
+ }
+
+ @Override public ImmutablePairList<T, U> immutable() {
+ return immutableBackedBy(castNonNullList(list));
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public <R> List<R> transform(BiFunction<T, U, R> function) {
+ return Functions.generate(list.size() / 2, index -> {
+ final int x = index * 2;
+ final T t = (T) list.get(x);
+ final U u = (U) list.get(x + 1);
+ return function.apply(t, u);
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public <R> ImmutableList<R> transform2(
+ BiFunction<T, U, R> function) {
+ if (list.isEmpty()) {
+ return ImmutableList.of();
+ }
+ final ImmutableList.Builder<R> builder = ImmutableList.builder();
+ for (int i = 0, n = list.size(); i < n;) {
+ final T t = (T) list.get(i++);
+ final U u = (U) list.get(i++);
+ builder.add(function.apply(t, u));
+ }
+ return builder.build();
+ }
+
+ @Override public PairList<T, U> subList(int fromIndex, int toIndex) {
+ return new MutablePairList<>(list.subList(fromIndex * 2, toIndex * 2));
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public boolean anyMatch(BiPredicate<T, U> predicate) {
+ for (int i = 0; i < list.size();) {
+ final T t = (T) list.get(i++);
+ final U u = (U) list.get(i++);
+ if (predicate.test(t, u)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public boolean allMatch(BiPredicate<T, U> predicate) {
+ for (int i = 0; i < list.size();) {
+ final T t = (T) list.get(i++);
+ final U u = (U) list.get(i++);
+ if (!predicate.test(t, u)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public boolean noMatch(BiPredicate<T, U> predicate) {
+ for (int i = 0; i < list.size();) {
+ final T t = (T) list.get(i++);
+ final U u = (U) list.get(i++);
+ if (predicate.test(t, u)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override public void reverse() {
+ for (int i = 0, j = list.size() - 2; i < j;) {
+ @Nullable Object o = list.get(i);
+ list.set(i, list.get(j));
+ list.set(j, o);
+ o = list.get(i + 1);
+ list.set(i + 1, list.get(j + 1));
+ list.set(j + 1, o);
+ i += 2;
+ j -= 2;
+ }
+ }
+
+ @Override public ImmutablePairList<T, U> reversed() {
+ if (size() <= 1) {
+ return immutable();
+ }
+ final ImmutableList.Builder<Object> b = ImmutableList.builder();
+ final List<Object> nonNullList = castNonNullList(list);
+ for (int j = list.size() - 2; j >= 0;) {
+ b.add(nonNullList.get(j));
+ b.add(nonNullList.get(j + 1));
+ j -= 2;
+ }
+ return PairLists.immutableBackedBy(b.build());
+ }
+ }
+
+ /** Empty immutable list of pairs.
+ *
+ * @param <T> First type
+ * @param <U> Second type
+ */
+ static class EmptyImmutablePairList<T, U>
+ extends AbstractPairList<T, U>
+ implements ImmutablePairList<T, U> {
+ @Override List<@Nullable Object> backingList() {
+ return ImmutableList.of();
+ }
+
+ @Override public Map.Entry<T, U> get(int index) {
+ throw new IndexOutOfBoundsException("Index out of range: " + index);
+ }
+
+ @Override public T left(int index) {
+ throw new IndexOutOfBoundsException("Index out of range: " + index);
+ }
+
+ @Override public U right(int index) {
+ throw new IndexOutOfBoundsException("Index out of range: " + index);
+ }
+
+ @Override public int size() {
+ return 0;
+ }
+
+ @Override public List<T> leftList() {
+ return ImmutableList.of();
+ }
+
+ @Override public List<U> rightList() {
+ return ImmutableList.of();
+ }
+
+ @Override public void forEach(BiConsumer<T, U> consumer) {
+ }
+
+ @Override public void forEachIndexed(IndexedBiConsumer<T, U> consumer) {
+ }
+
+ @Override public <R> List<R> transform(BiFunction<T, U, R> function) {
+ return ImmutableList.of();
+ }
+
+ @Override public <R> ImmutableList<R> transform2(
+ BiFunction<T, U, R> function) {
+ return ImmutableList.of();
+ }
+
+ @Override public ImmutablePairList<T, U> subList(int fromIndex,
+ int toIndex) {
+ subListRangeCheck(fromIndex, toIndex, size());
+ return this;
+ }
+
+ @Override public ImmutablePairList<T, U> reversed() {
+ return this;
+ }
+
+ @Override public boolean anyMatch(BiPredicate<T, U> predicate) {
+ return false;
+ }
+
+ @Override public boolean allMatch(BiPredicate<T, U> predicate) {
+ return true;
+ }
+
+ @Override public boolean noMatch(BiPredicate<T, U> predicate) {
+ return true;
+ }
+ }
+
+ /** Immutable list that contains one pair.
+ *
+ * @param <T> First type
+ * @param <U> Second type
+ */
+ static class SingletonImmutablePairList<T, U>
+ extends AbstractPairList<T, U>
+ implements ImmutablePairList<T, U> {
+ private final T t;
+ private final U u;
+
+ SingletonImmutablePairList(T t, U u) {
+ this.t = t;
+ this.u = u;
+ checkElementNotNull(0, t);
+ checkElementNotNull(1, u);
+ }
+
+ @Override List<@Nullable Object> backingList() {
+ return ImmutableList.of(t, u);
+ }
+
+ @Override public Map.Entry<T, U> get(int index) {
+ if (index != 0) {
+ throw new IndexOutOfBoundsException("Index out of range: " + index);
+ }
+ return new MapEntry<>(t, u);
+ }
+
+ @Override public T left(int index) {
+ if (index != 0) {
+ throw new IndexOutOfBoundsException("Index out of range: " + index);
+ }
+ return t;
+ }
+
+ @Override public U right(int index) {
+ if (index != 0) {
+ throw new IndexOutOfBoundsException("Index out of range: " + index);
+ }
+ return u;
+ }
+
+ @Override public int size() {
+ return 1;
+ }
+
+ @Override public List<T> leftList() {
+ return ImmutableList.of(t);
+ }
+
+ @Override public List<U> rightList() {
+ return ImmutableList.of(u);
+ }
+
+ @Override public void forEach(BiConsumer<T, U> consumer) {
+ consumer.accept(t, u);
+ }
+
+ @Override public void forEachIndexed(IndexedBiConsumer<T, U> consumer) {
+ consumer.accept(0, t, u);
+ }
+
+ @Override public <R> List<R> transform(BiFunction<T, U, R> function) {
+ return ImmutableList.of(function.apply(t, u));
+ }
+
+ @Override public <R> ImmutableList<R> transform2(
+ BiFunction<T, U, R> function) {
+ return ImmutableList.of(function.apply(t, u));
+ }
+
+ @Override public ImmutablePairList<T, U> subList(int fromIndex,
+ int toIndex) {
+ subListRangeCheck(fromIndex, toIndex, size());
+ return fromIndex > toIndex
+ ? this
+ : ImmutablePairList.of();
+ }
+
+ @Override public ImmutablePairList<T, U> reversed() {
+ return this;
+ }
+
+ @Override public boolean anyMatch(BiPredicate<T, U> predicate) {
+ return predicate.test(t, u);
+ }
+
+ @Override public boolean allMatch(BiPredicate<T, U> predicate) {
+ return predicate.test(t, u);
+ }
+
+ @Override public boolean noMatch(BiPredicate<T, U> predicate) {
+ return !predicate.test(t, u);
+ }
+ }
+
+ /** Base class for a list that implements {@link RandomAccess}.
+ *
+ * @param <E> Element type */
+ abstract static class RandomAccessList<E>
+ extends AbstractList<E> implements RandomAccess {
+ }
+
+ /** Immutable list of pairs backed by an array.
+ *
+ * @param <T> First type
+ * @param <U> Second type
+ */
+ static class ArrayImmutablePairList<T, U>
+ extends AbstractPairList<T, U>
+ implements ImmutablePairList<T, U> {
+ private final Object[] elements;
+
+ /** Creates an ArrayImmutablePairList.
+ *
+ * <p>Does not copy the {@code elements} array. Assumes that the caller has
+ * made a copy, and will never modify the contents.
+ *
+ * <p>Assumes that {@code elements} is not null, but checks that none of
+ * its elements are null. */
+ ArrayImmutablePairList(@Nullable Object[] elements) {
+ this.elements = checkElementsNotNull(elements);
+ }
+
+ @Override List<@Nullable Object> backingList() {
+ return Arrays.asList(elements);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public Map.Entry<T, U> get(int index) {
+ int x = index * 2;
+ return new MapEntry<>((T) elements[x], (U) elements[x + 1]);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public T left(int index) {
+ int x = index * 2;
+ return (T) elements[x];
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public U right(int index) {
+ int x = index * 2;
+ return (U) elements[x + 1];
+ }
+
+ @Override public int size() {
+ return elements.length / 2;
+ }
+
+ @Override public List<T> leftList() {
+ return new RandomAccessList<T>() {
+ @Override public int size() {
+ return elements.length / 2;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public T get(int index) {
+ return (T) elements[index * 2];
+ }
+ };
+ }
+
+ @Override public List<U> rightList() {
+ return new RandomAccessList<U>() {
+ @Override public int size() {
+ return elements.length / 2;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public U get(int index) {
+ return (U) elements[index * 2 + 1];
+ }
+ };
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public void forEach(BiConsumer<T, U> consumer) {
+ for (int x = 0; x < elements.length;) {
+ T t = (T) elements[x++];
+ U u = (U) elements[x++];
+ consumer.accept(t, u);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public void forEachIndexed(IndexedBiConsumer<T, U> consumer) {
+ for (int x = 0, i = 0; x < elements.length;) {
+ T t = (T) elements[x++];
+ U u = (U) elements[x++];
+ consumer.accept(i++, t, u);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public <R> List<R> transform(BiFunction<T, U, R> function) {
+ return Functions.generate(elements.length / 2, index -> {
+ final int x = index * 2;
+ final T t = (T) elements[x];
+ final U u = (U) elements[x + 1];
+ return function.apply(t, u);
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public <R> ImmutableList<R> transform2(
+ BiFunction<T, U, R> function) {
+ final ImmutableList.Builder<R> builder = ImmutableList.builder();
+ for (int i = 0; i < elements.length;) {
+ final T t = (T) elements[i++];
+ final U u = (U) elements[i++];
+ builder.add(function.apply(t, u));
+ }
+ return builder.build();
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public ImmutablePairList<T, U> subList(int fromIndex,
+ int toIndex) {
+ subListRangeCheck(fromIndex, toIndex, size());
+ switch (toIndex - fromIndex) {
+ case 0:
+ return ImmutablePairList.of();
+ case 2:
+ return new SingletonImmutablePairList<>((T) elements[fromIndex * 2],
+ (U) elements[fromIndex * 2 + 1]);
+ default:
+ return new ArrayImmutablePairList<>(
+ Arrays.copyOfRange(elements, fromIndex * 2,
+ toIndex * 2 - fromIndex * 2));
+ }
+ }
+
+ @Override public ImmutablePairList<T, U> reversed() {
+ if (size() <= 1) {
+ return immutable();
+ }
+ final Object[] elements2 = new Object[elements.length];
+ for (int j = elements.length - 2, i = 0; j >= 0;) {
+ elements2[i++] = elements[j];
+ elements2[i++] = elements[j + 1];
+ j -= 2;
+ }
+ return new ArrayImmutablePairList<>(elements2);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public boolean anyMatch(BiPredicate<T, U> predicate) {
+ for (int i = 0; i < elements.length;) {
+ final T t = (T) elements[i++];
+ final U u = (U) elements[i++];
+ if (predicate.test(t, u)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public boolean allMatch(BiPredicate<T, U> predicate) {
+ for (int i = 0; i < elements.length;) {
+ final T t = (T) elements[i++];
+ final U u = (U) elements[i++];
+ if (!predicate.test(t, u)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override public boolean noMatch(BiPredicate<T, U> predicate) {
+ for (int i = 0; i < elements.length;) {
+ final T t = (T) elements[i++];
+ final U u = (U) elements[i++];
+ if (predicate.test(t, u)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+}
diff --git a/core/src/main/java/org/apache/calcite/schema/Schemas.java
b/core/src/main/java/org/apache/calcite/schema/Schemas.java
index c007701bd2..4795473729 100644
--- a/core/src/main/java/org/apache/calcite/schema/Schemas.java
+++ b/core/src/main/java/org/apache/calcite/schema/Schemas.java
@@ -36,6 +36,8 @@ import org.apache.calcite.materialize.Lattice;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rel.type.RelProtoDataType;
+import org.apache.calcite.runtime.ImmutablePairList;
+import org.apache.calcite.runtime.PairList;
import org.apache.calcite.sql.type.SqlTypeUtil;
import org.apache.calcite.tools.RelRunner;
import org.apache.calcite.util.BuiltInMethod;
@@ -45,7 +47,6 @@ import org.apache.calcite.util.Util;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.Lists;
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -582,28 +583,29 @@ public final class Schemas {
}
}
- public static PathImpl path(ImmutableList<Pair<String, Schema>> build) {
- return new PathImpl(build);
+ public static Path path(Iterable<? extends Map.Entry<String, Schema>> list) {
+ return new PathImpl(ImmutablePairList.copyOf(list));
}
/** Returns the path to get to a schema from its root. */
public static Path path(SchemaPlus schema) {
- List<Pair<String, Schema>> list = new ArrayList<>();
+ PairList<String, Schema> list = PairList.of();
for (SchemaPlus s = schema; s != null; s = s.getParentSchema()) {
- list.add(Pair.of(s.getName(), s));
+ list.add(s.getName(), s);
}
- return new PathImpl(ImmutableList.copyOf(Lists.reverse(list)));
+ list.reverse();
+ return new PathImpl(list.immutable());
}
/** Implementation of {@link Path}. */
private static class PathImpl
extends AbstractList<Pair<String, Schema>> implements Path {
- private final ImmutableList<Pair<String, Schema>> pairs;
+ private final ImmutablePairList<String, Schema> pairs;
private static final PathImpl EMPTY =
- new PathImpl(ImmutableList.of());
+ new PathImpl(ImmutablePairList.of());
- PathImpl(ImmutableList<Pair<String, Schema>> pairs) {
+ PathImpl(ImmutablePairList<String, Schema> pairs) {
this.pairs = pairs;
}
@@ -618,7 +620,7 @@ public final class Schemas {
}
@Override public Pair<String, Schema> get(int index) {
- return pairs.get(index);
+ return Pair.of(pairs.get(index));
}
@Override public int size() {
@@ -633,19 +635,11 @@ public final class Schemas {
}
@Override public List<String> names() {
- return new AbstractList<String>() {
- @Override public String get(int index) {
- return pairs.get(index + 1).left;
- }
-
- @Override public int size() {
- return pairs.size() - 1;
- }
- };
+ return Util.skip(pairs.leftList());
}
@Override public List<Schema> schemas() {
- return Pair.right(pairs);
+ return pairs.rightList();
}
}
}
diff --git a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
index cd5a87bb8f..a0f4116523 100644
--- a/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
+++ b/core/src/main/java/org/apache/calcite/tools/RelBuilder.java
@@ -86,6 +86,7 @@ import org.apache.calcite.rex.RexUtil;
import org.apache.calcite.rex.RexWindowBound;
import org.apache.calcite.rex.RexWindowBounds;
import org.apache.calcite.runtime.Hook;
+import org.apache.calcite.runtime.ImmutablePairList;
import org.apache.calcite.runtime.PairList;
import org.apache.calcite.schema.TransientTable;
import org.apache.calcite.schema.impl.ListTransientTable;
@@ -601,23 +602,32 @@ public class RelBuilder {
public RexNode field(int inputCount, String alias, String fieldName) {
requireNonNull(alias, "alias");
requireNonNull(fieldName, "fieldName");
- final List<String> fields = new ArrayList<>();
for (int inputOrdinal = 0; inputOrdinal < inputCount; ++inputOrdinal) {
final Frame frame = peek_(inputOrdinal);
- for (Ord<Field> p
- : Ord.zip(frame.fields)) {
+ final List<ImmutableSet<String>> aliasSets = frame.fields.leftList();
+ final List<RelDataTypeField> fields = frame.fields.rightList();
+ for (int i = 0, n = frame.fields.size(); i < n; i++) {
// If alias and field name match, reference that field.
- if (p.e.left.contains(alias)
- && p.e.right.getName().equals(fieldName)) {
- return field(inputCount, inputCount - 1 - inputOrdinal, p.i);
+ if (aliasSets.get(i).contains(alias)
+ && fields.get(i).getName().equals(fieldName)) {
+ return field(inputCount, inputCount - 1 - inputOrdinal, i);
}
- fields.add(
- String.format(Locale.ROOT, "{aliases=%s,fieldName=%s}", p.e.left,
- p.e.right.getName()));
}
}
- throw new IllegalArgumentException("{alias=" + alias + ",fieldName=" +
fieldName + "} "
- + "field not found; fields are: " + fields);
+
+ // Not found. Build a message.
+ StringBuilder b =
+ new StringBuilder().append("{alias=").append(alias)
+ .append(",fieldName=").append(fieldName)
+ .append("} field not found; fields are: ");
+ for (int inputOrdinal = 0; inputOrdinal < inputCount; ++inputOrdinal) {
+ final Frame frame = peek_(inputOrdinal);
+ frame.fields.forEach((aliasSet, field) ->
+ b.append(
+ String.format(Locale.ROOT, "{aliases=%s,fieldName=%s}",
+ aliasSet, field.getName())));
+ }
+ throw new IllegalArgumentException(b.toString());
}
/** Returns a reference to a given field of a record-valued expression. */
@@ -2010,25 +2020,22 @@ public class RelBuilder {
// Carefully build a list of fields, so that table aliases from the input
// can be seen for fields that are based on a RexInputRef.
final Frame frame1 = stack.pop();
- final List<Field> fields = new ArrayList<>();
- for (RelDataTypeField f
- : project.getInput().getRowType().getFieldList()) {
- fields.add(new Field(ImmutableSet.of(), f));
- }
- for (Pair<RexNode, Field> pair
- : Pair.zip(project.getProjects(), frame1.fields)) {
+ final PairList<ImmutableSet<String>, RelDataTypeField> fields =
+ PairList.of();
+ project.getInput().getRowType().getFieldList()
+ .forEach(f -> fields.add(ImmutableSet.of(), f));
+ for (Pair<RexNode, ImmutableSet<String>> pair
+ : Pair.zip(project.getProjects(), frame1.fields.leftList())) {
switch (pair.left.getKind()) {
case INPUT_REF:
final int i = ((RexInputRef) pair.left).getIndex();
- final Field field = fields.get(i);
- final ImmutableSet<String> aliases = pair.right.left;
- fields.set(i, new Field(aliases, field.right));
+ fields.set(i, pair.right, fields.rightList().get(i));
break;
default:
break;
}
}
- stack.push(new Frame(project.getInput(), ImmutableList.copyOf(fields)));
+ stack.push(new Frame(project.getInput(), fields));
final ImmutableSet.Builder<RelHint> mergedHints = ImmutableSet.builder();
mergedHints.addAll(project.getHints());
mergedHints.addAll(hints);
@@ -2049,7 +2056,8 @@ public class RelBuilder {
}
}
- final ImmutableList.Builder<Field> fields = ImmutableList.builder();
+ final PairList<ImmutableSet<String>, RelDataTypeField> fields =
+ PairList.of();
final Set<String> uniqueNameList =
getTypeFactory().getTypeSystem().isSchemaCaseSensitive()
? new HashSet<>()
@@ -2059,7 +2067,6 @@ public class RelBuilder {
final RexNode node = nodeList.get(i);
String name = fieldNameList.get(i);
String originalName = name;
- Field field;
if (name == null || uniqueNameList.contains(name)) {
int j = 0;
if (name == null) {
@@ -2076,14 +2083,13 @@ public class RelBuilder {
case INPUT_REF:
// preserve rel aliases for INPUT_REF fields
final int index = ((RexInputRef) node).getIndex();
- field = new Field(frame.fields.get(index).left, fieldType);
+ fields.add(frame.fields.leftList().get(index), fieldType);
break;
default:
- field = new Field(ImmutableSet.of(), fieldType);
+ fields.add(ImmutableSet.of(), fieldType);
break;
}
uniqueNameList.add(name);
- fields.add(field);
}
if (!force && RexUtil.isIdentity(nodeList, inputRowType)) {
if (fieldNameList.equals(inputRowType.getFieldNames())) {
@@ -2093,7 +2099,7 @@ public class RelBuilder {
// create "virtual" row type for project only rename fields
stack.pop();
// Ignore the hints.
- stack.push(new Frame(frame.rel, fields.build()));
+ stack.push(new Frame(frame.rel, fields));
}
return this;
}
@@ -2122,7 +2128,7 @@ public class RelBuilder {
fieldNameList,
variables);
stack.pop();
- stack.push(new Frame(project, fields.build()));
+ stack.push(new Frame(project, fields));
return this;
}
@@ -2431,7 +2437,7 @@ public class RelBuilder {
assert groupSet.contains(set);
}
- List<Field> inFields = frame.fields;
+ PairList<ImmutableSet<String>, RelDataTypeField> inFields = frame.fields;
final ImmutableBitSet groupSet2;
final ImmutableList<ImmutableBitSet> groupSets2;
if (config.pruneInputOfAggregate()
@@ -2465,7 +2471,10 @@ public class RelBuilder {
for (AggregateCall aggregateCall : oldAggregateCalls) {
aggregateCalls.add(aggregateCall.transform(targetMapping));
}
- inFields = Mappings.permute(inFields, targetMapping.inverse());
+ final PairList<ImmutableSet<String>, RelDataTypeField> newInFields =
+ PairList.of();
+ newInFields.addAll(Mappings.permute(inFields,
targetMapping.inverse()));
+ inFields = newInFields;
final Project project = (Project) r;
final List<RexNode> newProjects = new ArrayList<>();
@@ -2561,13 +2570,14 @@ public class RelBuilder {
private RelBuilder aggregate_(ImmutableBitSet groupSet,
ImmutableList<ImmutableBitSet> groupSets, RelNode input,
List<AggregateCall> aggregateCalls, List<RexNode> extraNodes,
- List<Field> inFields) {
+ PairList<ImmutableSet<String>, RelDataTypeField> inFields) {
final RelNode aggregate =
struct.aggregateFactory.createAggregate(input,
ImmutableList.of(), groupSet, groupSets, aggregateCalls);
// build field list
- final ImmutableList.Builder<Field> fields = ImmutableList.builder();
+ final PairList<ImmutableSet<String>, RelDataTypeField> fields =
+ PairList.of();
final List<RelDataTypeField> aggregateFields =
aggregate.getRowType().getFieldList();
int i = 0;
@@ -2583,7 +2593,7 @@ public class RelBuilder {
String name = aggregateFields.get(i).getName();
RelDataTypeField fieldType =
new RelDataTypeFieldImpl(name, i, node.getType());
- fields.add(new Field(ImmutableSet.of(), fieldType));
+ fields.add(ImmutableSet.of(), fieldType);
break;
}
i++;
@@ -2594,9 +2604,9 @@ public class RelBuilder {
final RelDataTypeField fieldType =
new RelDataTypeFieldImpl(aggregateFields.get(i + j).getName(), i + j,
call.getType());
- fields.add(new Field(ImmutableSet.of(), fieldType));
+ fields.add(ImmutableSet.of(), fieldType);
}
- stack.push(new Frame(aggregate, fields.build()));
+ stack.push(new Frame(aggregate, fields));
return this;
}
@@ -2996,10 +3006,11 @@ public class RelBuilder {
join = join0;
}
}
- final ImmutableList.Builder<Field> fields = ImmutableList.builder();
+ final PairList<ImmutableSet<String>, RelDataTypeField> fields =
+ PairList.of();
fields.addAll(left.fields);
fields.addAll(right.fields);
- stack.push(new Frame(join, fields.build()));
+ stack.push(new Frame(join, fields));
filter(postCondition);
return this;
}
@@ -3031,10 +3042,11 @@ public class RelBuilder {
struct.correlateFactory.createCorrelate(left.rel, right.rel,
ImmutableList.of(),
correlationId, ImmutableBitSet.of(requiredOrdinals), joinType);
- final ImmutableList.Builder<Field> fields = ImmutableList.builder();
+ final PairList<ImmutableSet<String>, RelDataTypeField> fields =
+ PairList.of();
fields.addAll(left.fields);
fields.addAll(right.fields);
- stack.push(new Frame(correlate, fields.build()));
+ stack.push(new Frame(correlate, fields));
return this;
}
@@ -3136,9 +3148,17 @@ public class RelBuilder {
/** Assigns a table alias to the top entry on the stack. */
public RelBuilder as(final String alias) {
final Frame pair = stack.pop();
- List<Field> newFields =
- Util.transform(pair.fields, field -> field.addAlias(alias));
- stack.push(new Frame(pair.rel, ImmutableList.copyOf(newFields)));
+ final PairList<ImmutableSet<String>, RelDataTypeField> newFields =
+ PairList.of();
+ pair.fields.forEach((aliases, field) -> {
+ final ImmutableSet<String> aliasList =
+ aliases.contains(alias)
+ ? aliases
+ : ImmutableSet.<String>builder().addAll(aliases).add(alias)
+ .build();
+ newFields.add(aliasList, field);
+ });
+ stack.push(new Frame(pair.rel, newFields));
return this;
}
@@ -4690,24 +4710,27 @@ public class RelBuilder {
* information about how table aliases map into its row type. */
private static class Frame {
final RelNode rel;
- final ImmutableList<Field> fields;
+ final ImmutablePairList<ImmutableSet<String>, RelDataTypeField> fields;
- private Frame(RelNode rel, ImmutableList<Field> fields) {
+ private Frame(RelNode rel,
+ PairList<ImmutableSet<String>, RelDataTypeField> fields) {
this.rel = rel;
- this.fields = fields;
+ this.fields = fields.immutable();
}
private Frame(RelNode rel) {
String tableAlias = deriveAlias(rel);
- ImmutableList.Builder<Field> builder = ImmutableList.builder();
- ImmutableSet<String> aliases = tableAlias == null
- ? ImmutableSet.of()
- : ImmutableSet.of(tableAlias);
+ final PairList<ImmutableSet<String>, RelDataTypeField> fields =
+ PairList.of();
+ final ImmutableSet<String> aliases =
+ tableAlias == null
+ ? ImmutableSet.of()
+ : ImmutableSet.of(tableAlias);
for (RelDataTypeField field : rel.getRowType().getFieldList()) {
- builder.add(new Field(aliases, field));
+ fields.add(aliases, field);
}
this.rel = rel;
- this.fields = builder.build();
+ this.fields = fields.immutable();
}
@Override public String toString() {
@@ -4726,24 +4749,7 @@ public class RelBuilder {
}
List<RelDataTypeField> fields() {
- return Pair.right(fields);
- }
- }
-
- /** A field that belongs to a stack {@link Frame}. */
- private static class Field
- extends Pair<ImmutableSet<String>, RelDataTypeField> {
- Field(ImmutableSet<String> left, RelDataTypeField right) {
- super(left, right);
- }
-
- Field addAlias(String alias) {
- if (left.contains(alias)) {
- return this;
- }
- final ImmutableSet<String> aliasList =
- ImmutableSet.<String>builder().addAll(left).add(alias).build();
- return new Field(aliasList, right);
+ return fields.rightList();
}
}
@@ -4785,7 +4791,7 @@ public class RelBuilder {
/** Default configuration. */
Config DEFAULT = ImmutableRelBuilder.Config.of();
- /** Controls whether to merge two {@link Project} operators when inlining
+ /** Controls whether to merge two {@link Project} operators when inlining
* expressions causes complexity to increase.
*
* <p>Usually merging projects is beneficial, but occasionally the
diff --git a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
index f56493e22a..f0df061852 100644
--- a/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
+++ b/core/src/main/java/org/apache/calcite/util/BuiltInMethod.java
@@ -265,7 +265,10 @@ public enum BuiltInMethod {
FUNCTION1_APPLY(Function1.class, "apply", Object.class),
ARRAYS_AS_LIST(Arrays.class, "asList", Object[].class),
ARRAY(SqlFunctions.class, "array", Object[].class),
- PAIR_LIST_COPY_OF(PairList.class, "copyOf", Object.class, Object.class,
+ // class PairList.Helper is deprecated to discourage code from calling its
+ // methods directly, but use via Janino code generation is just fine.
+ @SuppressWarnings("deprecation")
+ PAIR_LIST_COPY_OF(PairList.Helper.class, "copyOf", Object.class,
Object.class,
Object[].class),
FLAT_PRODUCT(SqlFunctions.class, "flatProduct", int[].class, boolean.class,
FlatProductInputType[].class),
diff --git a/core/src/main/java/org/apache/calcite/util/Pair.java
b/core/src/main/java/org/apache/calcite/util/Pair.java
index 342e9310f7..c851ff4769 100644
--- a/core/src/main/java/org/apache/calcite/util/Pair.java
+++ b/core/src/main/java/org/apache/calcite/util/Pair.java
@@ -281,6 +281,35 @@ public class Pair<T1 extends @Nullable Object, T2 extends
@Nullable Object>
}
}
+ /** Calls a consumer with an ordinal for each pair of items in two
+ * iterables. */
+ public static <K, V> void forEachIndexed(Iterable<K> ks, Iterable<V> vs,
+ PairWithOrdinalConsumer<K, V> consumer) {
+ int i = 0;
+ final Iterator<K> ki = ks.iterator();
+ final Iterator<V> vi = vs.iterator();
+ while (ki.hasNext() && vi.hasNext()) {
+ consumer.accept(i++, ki.next(), vi.next());
+ }
+ }
+
+ /** Calls a consumer with an ordinal for each pair of items in an iterable
+ * of pairs. */
+ public static <K, V> void forEachIndexed(
+ Iterable<? extends Map.Entry<K, V>> pairs,
+ PairWithOrdinalConsumer<K, V> consumer) {
+ int i = 0;
+ for (Map.Entry<K, V> pair : pairs) {
+ consumer.accept(i++, pair.getKey(), pair.getValue());
+ }
+ }
+
+ /** Calls a consumer for each entry in a map. */
+ public static <K, V> void forEachIndexed(Map<K, V> map,
+ PairWithOrdinalConsumer<K, V> consumer) {
+ forEachIndexed(map.entrySet(), consumer);
+ }
+
/** Applies an action to every element of an iterable of pairs.
*
* @see Map#forEach(java.util.function.BiConsumer)
@@ -540,4 +569,26 @@ public class Pair<T1 extends @Nullable Object, T2 extends
@Nullable Object>
return previous;
}
}
+
+ /**
+ * Represents an operation that accepts two input arguments and an ordinal,
+ * and returns no result.
+ *
+ * <p>This is a specialization of {@link java.util.function.Consumer},
+ * similar to {@link BiConsumer}.
+ *
+ * @param <K> Key type
+ * @param <V> Value type
+ */
+ @FunctionalInterface
+ public interface PairWithOrdinalConsumer<K, V> {
+ /**
+ * Performs this operation on the given arguments.
+ *
+ * @param ordinal Ordinal
+ * @param k the first input argument
+ * @param v the second input argument
+ */
+ void accept(int ordinal, K k, V v);
+ }
}
diff --git a/core/src/test/java/org/apache/calcite/util/PairListTest.java
b/core/src/test/java/org/apache/calcite/util/PairListTest.java
index a6319575ec..1bafc852fb 100644
--- a/core/src/test/java/org/apache/calcite/util/PairListTest.java
+++ b/core/src/test/java/org/apache/calcite/util/PairListTest.java
@@ -16,9 +16,13 @@
*/
package org.apache.calcite.util;
+import org.apache.calcite.runtime.ImmutablePairList;
+import org.apache.calcite.runtime.MapEntry;
import org.apache.calcite.runtime.PairList;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Lists;
import org.junit.jupiter.api.Test;
@@ -36,7 +40,9 @@ import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.hasSize;
import static org.hamcrest.Matchers.hasToString;
+import static org.hamcrest.Matchers.startsWith;
import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.fail;
/** Unit test for {@code PairList}. */
class PairListTest {
@@ -67,6 +73,13 @@ class PairListTest {
assertThat(pairList.rightList(), is(right(list)));
assertThat(pairList.rightList(), instanceOf(RandomAccess.class));
+ // Check PairList.left(int) and PairList.right(int)
+ for (int i = 0; i < list.size(); i++) {
+ Map.Entry<T, U> entry = list.get(i);
+ assertThat(pairList.left(i), is(entry.getKey()));
+ assertThat(pairList.right(i), is(entry.getValue()));
+ }
+
final List<Map.Entry<T, U>> list2 = new ArrayList<>(pairList);
assertThat(list2, is(list));
@@ -91,19 +104,33 @@ class PairListTest {
// Check PairList.immutable()
// Skip if there are no null keys or values
- if (list.stream().noneMatch(e ->
- e.getKey() == null || e.getValue() == null)) {
+ if (list.stream().anyMatch(e -> e.getKey() == null)) {
+ // PairList.immutable should throw if there are null keys
+ try {
+ Object o = pairList.immutable();
+ fail("expected error, got " + o);
+ } catch (NullPointerException e) {
+ assertThat(e.getMessage(), startsWith("key at index"));
+ }
+ } else if (list.stream().anyMatch(e -> e.getValue() == null)) {
+ // PairList.immutable should throw if there are null values
+ try {
+ Object o = pairList.immutable();
+ fail("expected error, got " + o);
+ } catch (NullPointerException e) {
+ assertThat(e.getMessage(), startsWith("value at index"));
+ }
+ } else {
final PairList<T, U> immutablePairList = pairList.immutable();
assertThat(immutablePairList, hasSize(list.size()));
assertThat(immutablePairList, is(list));
+ assertThat(pairList.reversed(), is(Lists.reverse(list)));
+ assertThat(immutablePairList.reversed(), is(Lists.reverse(list)));
+
list2.clear();
immutablePairList.forEach((k, v) -> list2.add(Pair.of(k, v)));
assertThat(list2, is(list));
- } else {
- // PairList.immutable should throw if there are null keys or values
- assertThrows(NullPointerException.class,
- pairList::immutable);
}
}
@@ -187,6 +214,63 @@ class PairListTest {
validate(pairList, list);
}
+ @Test void testAddAll() {
+ PairList<String, Integer> pairList = PairList.of();
+
+ // MutablePairList (0 entries)
+ pairList.addAll(PairList.of());
+ assertThat(pairList, hasSize(0));
+
+ // MutablePairList (1 entry)
+ pairList.addAll(PairList.of("a", 1));
+ assertThat(pairList, hasSize(1));
+
+ // MutablePairList (2 entries)
+ pairList.addAll(PairList.of(ImmutableMap.of("b", 2, "c", 3)));
+ assertThat(pairList, hasSize(3));
+
+ // EmptyImmutablePairList
+ pairList.addAll(ImmutablePairList.of());
+ assertThat(pairList, hasSize(3));
+
+ // ImmutableList (0 entries)
+ pairList.addAll(ImmutableList.of());
+ assertThat(pairList, hasSize(3));
+
+ // SingletonImmutablePairList
+ pairList.addAll(ImmutablePairList.of("d", 4));
+ assertThat(pairList, hasSize(4));
+
+ // ImmutableList (1 entry)
+ pairList.addAll(ImmutableList.of(new MapEntry<>("e", 5)));
+ assertThat(pairList, hasSize(5));
+
+ // MutablePairList (2 entries)
+ pairList.addAll(PairList.copyOf("f", 6, "g", 7));
+ assertThat(pairList, hasSize(7));
+
+ // ArrayImmutablePairList (2 entries, created from MutablePairList)
+ pairList.addAll(PairList.copyOf("h", 8, "i", 9).immutable());
+ assertThat(pairList, hasSize(9));
+
+ // ArrayImmutablePairList (3 entries, created using copyOf)
+ pairList.addAll(ImmutablePairList.copyOf("j", 10, "k", 11, "l", 12));
+ assertThat(pairList, hasSize(12));
+
+ // ArrayImmutablePairList (2 entries, created using copyOf)
+ pairList.addAll(ImmutablePairList.copyOf("m", 13, "n", 14));
+ assertThat(pairList, hasSize(14));
+
+ // ArrayImmutablePairList (1 entry, created using copyOf)
+ pairList.addAll(ImmutablePairList.copyOf("o", 15));
+ assertThat(pairList, hasSize(15));
+
+ assertThat(pairList,
+ hasToString("[<a, 1>, <b, 2>, <c, 3>, <d, 4>, <e, 5>, <f, 6>, "
+ + "<g, 7>, <h, 8>, <i, 9>, <j, 10>, <k, 11>, <l, 12>, "
+ + "<m, 13>, <n, 14>, <o, 15>]"));
+ }
+
/** Tests {@link PairList#of(Map)} and {@link PairList#toImmutableMap()}. */
@Test void testPairListOfMap() {
final ImmutableMap<String, Integer> map = ImmutableMap.of("a", 1, "b", 2);
@@ -258,6 +342,8 @@ class PairListTest {
PairList.copyOf("a", 1, null, 5, "c", 3);
assertThat(list3.transform((s, i) -> s + i),
is(Arrays.asList("a1", "null5", "c3")));
+ assertThat(list3.transform2((s, i) -> s + i),
+ is(Arrays.asList("a1", "null5", "c3")));
final PairList<String, Integer> list0 = PairList.of();
assertThat(list0.transform((s, i) -> s + i), empty());
@@ -297,6 +383,9 @@ class PairListTest {
final PairList<String, Integer> list0 = b.build();
validate(list0, list);
+ final ImmutablePairList<String, Integer> list0i = b.buildImmutable();
+ validate(list0i, list);
+
b.add("a", 1);
list.add(Pair.of("a", 1));
final PairList<String, Integer> list1 = b.build();
@@ -308,5 +397,65 @@ class PairListTest {
list.add(Pair.of("c", null));
final PairList<String, Integer> list3 = b.build();
validate(list3, list);
+
+ // Reverse PairList in place
+ list3.reverse();
+ validate(list3, Lists.reverse(list));
+
+ // Singleton list with null key
+ final PairList.Builder<String, Integer> b2 = PairList.builder();
+ list.clear();
+ b2.add(null, 5);
+ list.add(Pair.of(null, 5));
+ validate(b2.build(), list);
+
+ // Singleton list with null value
+ final PairList.Builder<String, Integer> b3 = PairList.builder();
+ list.clear();
+ b3.add("x", null);
+ list.add(Pair.of("x", null));
+ validate(b3.build(), list);
+ }
+
+ @Test void testReversed() {
+ final PairList<Integer, Integer> list = PairList.of();
+ assertThat("empty list", list, hasToString("[]"));
+
+ list.reverse();
+ assertThat("empty list, reversed", list, hasToString("[]"));
+ assertThat(list.reversed(), is(Lists.reverse(list)));
+ assertThat(list.reversed().reversed(), is(list));
+
+ list.add(1, 2);
+ list.reverse();
+ assertThat("singleton list, reversed", list, hasToString("[<1, 2>]"));
+ assertThat(list.reversed(), is(Lists.reverse(list)));
+ assertThat(list.reversed().reversed(), is(list));
+
+ list.reverse();
+ assertThat("singleton list reversed twice", list,
+ hasToString("[<1, 2>]"));
+ assertThat(list.reversed(), is(Lists.reverse(list)));
+ assertThat(list.reversed().reversed(), is(list));
+
+ list.add(3, 4);
+ list.reverse();
+ assertThat("list with even length, reversed", list,
+ hasToString("[<3, 4>, <1, 2>]"));
+ assertThat(list.reversed(), is(Lists.reverse(list)));
+ assertThat(list.reversed().reversed(), is(list));
+
+ list.reverse();
+ assertThat("list with even length, reversed twice", list,
+ hasToString("[<1, 2>, <3, 4>]"));
+ assertThat(list.reversed(), is(Lists.reverse(list)));
+ assertThat(list.reversed().reversed(), is(list));
+
+ list.add(5, 6);
+ list.reverse();
+ assertThat("list with odd length, reversed", list,
+ hasToString("[<5, 6>, <3, 4>, <1, 2>]"));
+ assertThat(list.reversed(), is(Lists.reverse(list)));
+ assertThat(list.reversed().reversed(), is(list));
}
}
diff --git
a/testkit/src/main/java/org/apache/calcite/test/catalog/MockCatalogReader.java
b/testkit/src/main/java/org/apache/calcite/test/catalog/MockCatalogReader.java
index 19b68fa775..8f7e8f5ca9 100644
---
a/testkit/src/main/java/org/apache/calcite/test/catalog/MockCatalogReader.java
+++
b/testkit/src/main/java/org/apache/calcite/test/catalog/MockCatalogReader.java
@@ -115,6 +115,7 @@ public abstract class MockCatalogReader extends
CalciteCatalogReader {
static final String DEFAULT_CATALOG = "CATALOG";
static final String DEFAULT_SCHEMA = "SALES";
static final List<String> PREFIX = ImmutableList.of(DEFAULT_SCHEMA);
+ private static final Schema DUMMY_SCHEMA = new AbstractSchema();
/**
* Creates a MockCatalogReader.
@@ -863,12 +864,9 @@ public abstract class MockCatalogReader extends
CalciteCatalogReader {
}
@Override public Path getTablePath() {
- final ImmutableList.Builder<Pair<String, Schema>> builder =
- ImmutableList.builder();
- for (String name : fromTable.names) {
- builder.add(Pair.of(name, null));
- }
- return Schemas.path(builder.build());
+ final PairList<String, Schema> list = PairList.of();
+ fromTable.names.forEach(name -> list.add(name, DUMMY_SCHEMA));
+ return Schemas.path(list);
}
@Override public ImmutableIntList getColumnMapping() {