/*
* 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.eigenbase.util;
import java.io.Serializable;
import java.util.*;
import com.google.common.collect.Iterators;
/**
* Pair of objects.
*
* <p>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}.
*/
public class Pair<T1, T2>
implements Comparable<Pair<T1, T2>>, Map.Entry<T1, T2>, 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.
*
* <p>This is a shorthand that allows you to omit implicit types. For
* example, you can write:
* <blockquote>return Pair.of(s, n);</blockquote>
* instead of
* <blockquote>return new Pair<String, Integer>(s, n);</blockquote>
*
* @param left left value
* @param right right value
* @return A Pair
*/
public static <T1, T2> Pair<T1, T2> of(T1 left, T2 right) {
return new Pair<T1, T2>(left, right);
}
//~ Methods ----------------------------------------------------------------
public boolean equals(Object obj) {
return this == obj
|| (obj instanceof Pair)
&& Util.equal(this.left, ((Pair) obj).left)
&& Util.equal(this.right, ((Pair) obj).right);
}
public int hashCode() {
int h1 = Util.hash(0, left);
return Util.hash(h1, right);
}
public int compareTo(Pair<T1, T2> that) {
int c = compare((Comparable) this.left, (Comparable) that.left);
if (c == 0) {
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 <C extends Comparable<C>> 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.
*
* <p>This is an obvious thing to do because Pair is similar in structure to
* {@link java.util.Map.Entry}.
*
* <p>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 <K, V> Map<K, V> toMap(Collection<Pair<K, V>> pairs) {
HashMap<K, V> map = new HashMap<K, V>();
for (Pair<K, V> pair : pairs) {
map.put(pair.left, pair.right);
}
return map;
}
/**
* Converts two lists into a list of {@link org.eigenbase.util.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 net.hydromatic.linq4j.Ord#zip(java.util.List)
*/
public static <K, V> List<Pair<K, V>> zip(List<K> ks, List<V> vs) {
return zip(ks, vs, false);
}
/**
* Converts two lists into a list of {@link Pair}s.
*
* <p>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.</p>
*
* @param ks Left list
* @param vs Right list
* @param strict Whether to fail if lists have different size
* @return List of pairs
* @see net.hydromatic.linq4j.Ord#zip(java.util.List)
*/
public static <K, V> List<Pair<K, V>> zip(
final List<K> ks,
final List<V> 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<Pair<K, V>>() {
public Pair<K, V> 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.
*
* <p>The resulting iterator ends whenever the first of the input iterators
* ends. But typically the source iterators will be the same length.</p>
*
* @param ks Left iterable
* @param vs Right iterable
* @return Iterable over pairs
*/
public static <K, V> Iterable<Pair<K, V>> zip(
final Iterable<K> ks,
final Iterable<V> vs) {
return new Iterable<Pair<K, V>>() {
public Iterator<Pair<K, V>> iterator() {
final Iterator<K> kIterator = ks.iterator();
final Iterator<V> vIterator = vs.iterator();
return new Iterator<Pair<K, V>>() {
public boolean hasNext() {
return kIterator.hasNext() && vIterator.hasNext();
}
public Pair<K, V> next() {
return Pair.of(kIterator.next(), vIterator.next());
}
public void remove() {
kIterator.remove();
vIterator.remove();
}
};
}
};
}
/**
* Converts two arrays into a list of {@link Pair}s.
*
* <p>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.</p>
*
* @param ks Left array
* @param vs Right array
* @return List of pairs
*/
public static <K, V> List<Pair<K, V>> zip(
final K[] ks,
final V[] vs) {
return new AbstractList<Pair<K, V>>() {
public Pair<K, V> 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 <L> Left type
* @param <R> Right type
* @return Iterable over the left elements
*/
public static <L, R> Iterable<L> left(
final Iterable<? extends Map.Entry<L, R>> iterable) {
return new Iterable<L>() {
public Iterator<L> iterator() {
final Iterator<? extends Map.Entry<L, R>> iterator =
iterable.iterator();
return new Iterator<L>() {
public boolean hasNext() {
return iterator.hasNext();
}
public L next() {
return iterator.next().getKey();
}
public void remove() {
iterator.remove();
}
};
}
};
}
/**
* Returns an iterable over the right slice of an iterable.
*
* @param iterable Iterable over pairs
* @param <L> right type
* @param <R> Right type
* @return Iterable over the right elements
*/
public static <L, R> Iterable<R> right(
final Iterable<? extends Map.Entry<L, R>> iterable) {
return new Iterable<R>() {
public Iterator<R> iterator() {
final Iterator<? extends Map.Entry<L, R>> iterator =
iterable.iterator();
return new Iterator<R>() {
public boolean hasNext() {
return iterator.hasNext();
}
public R next() {
return iterator.next().getValue();
}
public void remove() {
iterator.remove();
}
};
}
};
}
public static <K, V> List<K> left(
final List<? extends Map.Entry<K, V>> pairs) {
return new AbstractList<K>() {
public K get(int index) {
return pairs.get(index).getKey();
}
public int size() {
return pairs.size();
}
};
}
public static <K, V> List<V> right(
final List<? extends Map.Entry<K, V>> pairs) {
return new AbstractList<V>() {
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.
*
* <p>For example, {@code adjacents([3, 5, 7])} returns [(3, 5), (5, 7)].</p>
*
* @param iterable Source collection
* @param <T> Element type
* @return Iterable over adjacent element pairs
*/
public static <T> Iterable<Pair<T, T>> adjacents(final Iterable<T> iterable) {
return new Iterable<Pair<T, T>>() {
public Iterator<Pair<T, T>> iterator() {
final Iterator<T> iterator = iterable.iterator();
if (!iterator.hasNext()) {
return Iterators.emptyIterator();
}
final T first = iterator.next();
return new Iterator<Pair<T, T>>() {
T previous = first;
public boolean hasNext() {
return iterator.hasNext();
}
public Pair<T, T> next() {
final T current = iterator.next();
final Pair<T, T> pair = of(previous, current);
previous = current;
return pair;
}
public void remove() {
throw new UnsupportedOperationException("remove");
}
};
}
};
}
/**
* Returns an iterator that iterates over (0, i) pairs in an iterable for
* i > 0.
*
* <p>For example, {@code firstAnd([3, 5, 7])} returns [(3, 5), (3, 7)].</p>
*
* @param iterable Source collection
* @param <T> Element type
* @return Iterable over pairs of the first element and all other elements
*/
public static <T> Iterable<Pair<T, T>> firstAnd(final Iterable<T> iterable) {
return new Iterable<Pair<T, T>>() {
public Iterator<Pair<T, T>> iterator() {
final Iterator<T> iterator = iterable.iterator();
if (!iterator.hasNext()) {
return Iterators.emptyIterator();
}
final T first = iterator.next();
return new Iterator<Pair<T, T>>() {
public boolean hasNext() {
return iterator.hasNext();
}
public Pair<T, T> next() {
return of(first, iterator.next());
}
public void remove() {
throw new UnsupportedOperationException("remove");
}
};
}
};
}
}
// End Pair.java