org.apache.calcite.util.Pair Maven / Gradle / Ivy
Show all versions of calcite-core Show documentation
/*
* 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.util;
import java.io.Serializable;
import java.util.AbstractList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import javax.annotation.Nonnull;
/**
* Pair of objects.
*
* Because a pair implements {@link #equals(Object)}, {@link #hashCode()} and
* {@link #compareTo(Pair)}, it can be used in any kind of
* {@link java.util.Collection}.
*
* @param Left-hand type
* @param Right-hand type
*/
public class Pair
implements Comparable>, Map.Entry, Serializable {
//~ Instance fields --------------------------------------------------------
public final T1 left;
public final T2 right;
//~ Constructors -----------------------------------------------------------
/**
* Creates a Pair.
*
* @param left left value
* @param right right value
*/
public Pair(T1 left, T2 right) {
this.left = left;
this.right = right;
}
/**
* Creates a Pair of appropriate type.
*
* This is a shorthand that allows you to omit implicit types. For
* example, you can write:
*
return Pair.of(s, n);
* instead of
* return new Pair<String, Integer>(s, n);
*
* @param left left value
* @param right right value
* @return A Pair
*/
public static Pair of(T1 left, T2 right) {
return new Pair<>(left, right);
}
/** Creates a {@code Pair} from a {@link java.util.Map.Entry}. */
public static Pair of(Map.Entry entry) {
return of(entry.getKey(), entry.getValue());
}
//~ Methods ----------------------------------------------------------------
public boolean equals(Object obj) {
return this == obj
|| (obj instanceof Pair)
&& Objects.equals(this.left, ((Pair) obj).left)
&& Objects.equals(this.right, ((Pair) obj).right);
}
/** {@inheritDoc}
*
* Computes hash code consistent with
* {@link java.util.Map.Entry#hashCode()}. */
@Override public int hashCode() {
int keyHash = left == null ? 0 : left.hashCode();
int valueHash = right == null ? 0 : right.hashCode();
return keyHash ^ valueHash;
}
public int compareTo(@Nonnull Pair that) {
//noinspection unchecked
int c = compare((Comparable) this.left, (Comparable) that.left);
if (c == 0) {
//noinspection unchecked
c = compare((Comparable) this.right, (Comparable) that.right);
}
return c;
}
public String toString() {
return "<" + left + ", " + right + ">";
}
public T1 getKey() {
return left;
}
public T2 getValue() {
return right;
}
public T2 setValue(T2 value) {
throw new UnsupportedOperationException();
}
/**
* Compares a pair of comparable values of the same type. Null collates
* less than everything else, but equal to itself.
*
* @param c1 First value
* @param c2 Second value
* @return a negative integer, zero, or a positive integer if c1
* is less than, equal to, or greater than c2.
*/
private static > int compare(C c1, C c2) {
if (c1 == null) {
if (c2 == null) {
return 0;
} else {
return -1;
}
} else if (c2 == null) {
return 1;
} else {
return c1.compareTo(c2);
}
}
/**
* Converts a collection of Pairs into a Map.
*
* This is an obvious thing to do because Pair is similar in structure to
* {@link java.util.Map.Entry}.
*
*
The map contains a copy of the collection of Pairs; if you change the
* collection, the map does not change.
*
* @param pairs Collection of Pair objects
* @return map with the same contents as the collection
*/
public static Map toMap(Iterable> pairs) {
final Map map = new HashMap<>();
for (Pair pair : pairs) {
map.put(pair.left, pair.right);
}
return map;
}
/**
* Converts two lists into a list of {@link Pair}s,
* whose length is the lesser of the lengths of the
* source lists.
*
* @param ks Left list
* @param vs Right list
* @return List of pairs
* @see org.apache.calcite.linq4j.Ord#zip(java.util.List)
*/
public static List> zip(List ks, List vs) {
return zip(ks, vs, false);
}
/**
* Converts two lists into a list of {@link Pair}s.
*
* The length of the combined list is the lesser of the lengths of the
* source lists. But typically the source lists will be the same length.
*
* @param ks Left list
* @param vs Right list
* @param strict Whether to fail if lists have different size
* @return List of pairs
* @see org.apache.calcite.linq4j.Ord#zip(java.util.List)
*/
public static List> zip(
final List ks,
final List vs,
boolean strict) {
final int size;
if (strict) {
if (ks.size() != vs.size()) {
throw new AssertionError();
}
size = ks.size();
} else {
size = Math.min(ks.size(), vs.size());
}
return new AbstractList>() {
public Pair get(int index) {
return Pair.of(ks.get(index), vs.get(index));
}
public int size() {
return size;
}
};
}
/**
* Converts two iterables into an iterable of {@link Pair}s.
*
* The resulting iterator ends whenever the first of the input iterators
* ends. But typically the source iterators will be the same length.
*
* @param ks Left iterable
* @param vs Right iterable
* @return Iterable over pairs
*/
public static Iterable> zip(
final Iterable extends K> ks,
final Iterable extends V> vs) {
return () -> {
final Iterator extends K> kIterator = ks.iterator();
final Iterator extends V> vIterator = vs.iterator();
return new ZipIterator<>(kIterator, vIterator);
};
}
/**
* Converts two arrays into a list of {@link Pair}s.
*
* The length of the combined list is the lesser of the lengths of the
* source arrays. But typically the source arrays will be the same
* length.
*
* @param ks Left array
* @param vs Right array
* @return List of pairs
*/
public static List> zip(
final K[] ks,
final V[] vs) {
return new AbstractList>() {
public Pair get(int index) {
return Pair.of(ks[index], vs[index]);
}
public int size() {
return Math.min(ks.length, vs.length);
}
};
}
/**
* Returns an iterable over the left slice of an iterable.
*
* @param iterable Iterable over pairs
* @param Left type
* @param Right type
* @return Iterable over the left elements
*/
public static Iterable left(
final Iterable extends Map.Entry> iterable) {
return () -> new LeftIterator<>(iterable.iterator());
}
/**
* Returns an iterable over the right slice of an iterable.
*
* @param iterable Iterable over pairs
* @param right type
* @param Right type
* @return Iterable over the right elements
*/
public static Iterable right(
final Iterable extends Map.Entry> iterable) {
return () -> new RightIterator<>(iterable.iterator());
}
public static List left(
final List extends Map.Entry> pairs) {
return new AbstractList() {
public K get(int index) {
return pairs.get(index).getKey();
}
public int size() {
return pairs.size();
}
};
}
public static List right(
final List extends Map.Entry> pairs) {
return new AbstractList() {
public V get(int index) {
return pairs.get(index).getValue();
}
public int size() {
return pairs.size();
}
};
}
/**
* Returns an iterator that iterates over (i, i + 1) pairs in an iterable.
*
* For example, {@code adjacents([3, 5, 7])} returns [(3, 5), (5, 7)].
*
* @param iterable Source collection
* @param Element type
* @return Iterable over adjacent element pairs
*/
public static Iterable> adjacents(final Iterable iterable) {
return () -> {
final Iterator iterator = iterable.iterator();
if (!iterator.hasNext()) {
return Collections.emptyIterator();
}
return new AdjacentIterator<>(iterator);
};
}
/**
* Returns an iterator that iterates over (0, i) pairs in an iterable for
* i > 0.
*
* For example, {@code firstAnd([3, 5, 7])} returns [(3, 5), (3, 7)].
*
* @param iterable Source collection
* @param Element type
* @return Iterable over pairs of the first element and all other elements
*/
public static Iterable> firstAnd(final Iterable iterable) {
return () -> {
final Iterator iterator = iterable.iterator();
if (!iterator.hasNext()) {
return Collections.emptyIterator();
}
final T first = iterator.next();
return new FirstAndIterator<>(iterator, first);
};
}
/** Iterator that returns the left field of each pair.
*
* @param Left-hand type
* @param Right-hand type */
private static class LeftIterator implements Iterator {
private final Iterator extends Map.Entry> iterator;
LeftIterator(Iterator extends Map.Entry> iterator) {
this.iterator = Objects.requireNonNull(iterator);
}
public boolean hasNext() {
return iterator.hasNext();
}
public L next() {
return iterator.next().getKey();
}
public void remove() {
iterator.remove();
}
}
/** Iterator that returns the right field of each pair.
*
* @param Left-hand type
* @param Right-hand type */
private static class RightIterator implements Iterator {
private final Iterator extends Map.Entry> iterator;
RightIterator(Iterator extends Map.Entry> iterator) {
this.iterator = Objects.requireNonNull(iterator);
}
public boolean hasNext() {
return iterator.hasNext();
}
public R next() {
return iterator.next().getValue();
}
public void remove() {
iterator.remove();
}
}
/** Iterator that returns the first element of a collection paired with every
* other element.
*
* @param Element type */
private static class FirstAndIterator implements Iterator> {
private final Iterator iterator;
private final E first;
FirstAndIterator(Iterator iterator, E first) {
this.iterator = Objects.requireNonNull(iterator);
this.first = first;
}
public boolean hasNext() {
return iterator.hasNext();
}
public Pair next() {
return of(first, iterator.next());
}
public void remove() {
throw new UnsupportedOperationException("remove");
}
}
/** Iterator that pairs elements from two iterators.
*
* @param Left-hand type
* @param Right-hand type */
private static class ZipIterator implements Iterator> {
private final Iterator extends L> leftIterator;
private final Iterator extends R> rightIterator;
ZipIterator(Iterator extends L> leftIterator,
Iterator extends R> rightIterator) {
this.leftIterator = Objects.requireNonNull(leftIterator);
this.rightIterator = Objects.requireNonNull(rightIterator);
}
public boolean hasNext() {
return leftIterator.hasNext() && rightIterator.hasNext();
}
public Pair next() {
return Pair.of(leftIterator.next(), rightIterator.next());
}
public void remove() {
leftIterator.remove();
rightIterator.remove();
}
}
/** Iterator that returns consecutive pairs of elements from an underlying
* iterator.
*
* @param Element type */
private static class AdjacentIterator implements Iterator> {
private final E first;
private final Iterator iterator;
E previous;
AdjacentIterator(Iterator iterator) {
this.iterator = Objects.requireNonNull(iterator);
this.first = iterator.next();
previous = first;
}
public boolean hasNext() {
return iterator.hasNext();
}
public Pair next() {
final E current = iterator.next();
final Pair pair = of(previous, current);
previous = current;
return pair;
}
public void remove() {
throw new UnsupportedOperationException("remove");
}
}
}
// End Pair.java