package edu.stanford.nlp.util; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.Serializable; import java.util.Comparator; import java.util.List; import edu.stanford.nlp.util.logging.PrettyLoggable; import edu.stanford.nlp.util.logging.PrettyLogger; import edu.stanford.nlp.util.logging.Redwood.RedwoodChannels; /** * Pair is a Class for holding mutable pairs of objects. * * <i>Implementation note:</i> * on a 32-bit JVM uses ~ 8 (this) + 4 (first) + 4 (second) = 16 bytes. * on a 64-bit JVM uses ~ 16 (this) + 8 (first) + 8 (second) = 32 bytes. * * Many applications use a lot of Pairs so it's good to keep this * number small. * * @author Dan Klein * @author Christopher Manning (added stuff from Kristina's, rounded out) * @version 2002/08/25 */ public class Pair <T1,T2> implements Comparable<Pair<T1,T2>>, Serializable, PrettyLoggable { /** * Direct access is deprecated. Use first(). * * @serial */ public T1 first; /** * Direct access is deprecated. Use second(). * * @serial */ public T2 second; public Pair() { // first = null; second = null; -- default initialization } public Pair(T1 first, T2 second) { this.first = first; this.second = second; } public T1 first() { return first; } public T2 second() { return second; } public void setFirst(T1 o) { first = o; } public void setSecond(T2 o) { second = o; } @Override public String toString() { return "(" + first + "," + second + ")"; } @Override public boolean equals(Object o) { if (o instanceof Pair) { @SuppressWarnings("rawtypes") Pair p = (Pair) o; return (first == null ? p.first() == null : first.equals(p.first())) && (second == null ? p.second() == null : second.equals(p.second())); } else { return false; } } @Override public int hashCode() { int firstHash = (first == null ? 0 : first.hashCode()); int secondHash = (second == null ? 0 : second.hashCode()); return firstHash*31 + secondHash; } public List<Object> asList() { return CollectionUtils.makeList(first, second); } /** * Read a string representation of a Pair from a DataStream. * This might not work correctly unless the pair of objects are of type * <code>String</code>. */ public static Pair<String, String> readStringPair(DataInputStream in) { Pair<String, String> p = new Pair<>(); try { p.first = in.readUTF(); p.second = in.readUTF(); } catch (Exception e) { e.printStackTrace(); } return p; } /** * Returns a Pair constructed from X and Y. Convenience method; the * compiler will disambiguate the classes used for you so that you * don't have to write out potentially long class names. */ public static <X, Y> Pair<X, Y> makePair(X x, Y y) { return new Pair<>(x, y); } /** * Write a string representation of a Pair to a DataStream. * The <code>toString()</code> method is called on each of the pair * of objects and a <code>String</code> representation is written. * This might not allow one to recover the pair of objects unless they * are of type <code>String</code>. */ public void save(DataOutputStream out) { try { out.writeUTF(first.toString()); out.writeUTF(second.toString()); } catch (Exception e) { e.printStackTrace(); } } /** * Compares this <code>Pair</code> to another object. * If the object is a <code>Pair</code>, this function will work providing * the elements of the <code>Pair</code> are themselves comparable. * It will then return a value based on the pair of objects, where * <code>p > q iff p.first() > q.first() || * (p.first().equals(q.first()) && p.second() > q.second())</code>. * If the other object is not a <code>Pair</code>, it throws a * <code>ClassCastException</code>. * * @param another the <code>Object</code> to be compared. * @return the value <code>0</code> if the argument is a * <code>Pair</code> equal to this <code>Pair</code>; a value less than * <code>0</code> if the argument is a <code>Pair</code> * greater than this <code>Pair</code>; and a value * greater than <code>0</code> if the argument is a * <code>Pair</code> less than this <code>Pair</code>. * @throws ClassCastException if the argument is not a * <code>Pair</code>. * @see java.lang.Comparable */ @SuppressWarnings("unchecked") public int compareTo(Pair<T1,T2> another) { if (first() instanceof Comparable) { int comp = ((Comparable<T1>) first()).compareTo(another.first()); if (comp != 0) { return comp; } } if (second() instanceof Comparable) { return ((Comparable<T2>) second()).compareTo(another.second()); } if ((!(first() instanceof Comparable)) && (!(second() instanceof Comparable))) { throw new AssertionError("Neither element of pair comparable"); } return 0; } /** * If first and second are Strings, then this returns an MutableInternedPair * where the Strings have been interned, and if this Pair is serialized * and then deserialized, first and second are interned upon * deserialization. * * @param p A pair of Strings * @return MutableInternedPair, with same first and second as this. */ public static Pair<String, String> stringIntern(Pair<String, String> p) { return new MutableInternedPair(p); } /** * Returns an MutableInternedPair where the Strings have been interned. * This is a factory method for creating an * MutableInternedPair. It requires the arguments to be Strings. * If this Pair is serialized * and then deserialized, first and second are interned upon * deserialization. * <p><i>Note:</i> I put this in thinking that its use might be * faster than calling <code>x = new Pair(a, b).stringIntern()</code> * but it's not really clear whether this is true. * * @param first The first object * @param second The second object * @return An MutableInternedPair, with given first and second */ public static Pair<String, String> internedStringPair(String first, String second) { return new MutableInternedPair(first, second); } /** * use serialVersionUID for cross version serialization compatibility */ private static final long serialVersionUID = 1360822168806852921L; static class MutableInternedPair extends Pair<String, String> { private MutableInternedPair(Pair<String, String> p) { super(p.first, p.second); internStrings(); } private MutableInternedPair(String first, String second) { super(first, second); internStrings(); } protected Object readResolve() { internStrings(); return this; } private void internStrings() { if (first != null) { first = first.intern(); } if (second != null) { second = second.intern(); } } // use serialVersionUID for cross version serialization compatibility private static final long serialVersionUID = 1360822168806852922L; } /** * {@inheritDoc} */ public void prettyLog(RedwoodChannels channels, String description) { PrettyLogger.log(channels, description, this.asList()); } /** * Compares a <code>Pair</code> to another <code>Pair</code> according to the first object of the pair only * This function will work providing * the first element of the <code>Pair</code> is comparable, otherwise will throw a * <code>ClassCastException</code> * @author jonathanberant * * @param <T1> * @param <T2> */ public static class ByFirstPairComparator<T1,T2> implements Comparator<Pair<T1,T2>> { @SuppressWarnings("unchecked") @Override public int compare(Pair<T1, T2> pair1, Pair<T1, T2> pair2) { return ((Comparable<T1>) pair1.first()).compareTo(pair2.first()); } } /** * Compares a <code>Pair</code> to another <code>Pair</code> according to the first object of the pair only in decreasing order * This function will work providing * the first element of the <code>Pair</code> is comparable, otherwise will throw a * <code>ClassCastException</code> * @author jonathanberant * * @param <T1> * @param <T2> */ public static class ByFirstReversePairComparator<T1,T2> implements Comparator<Pair<T1,T2>> { @SuppressWarnings("unchecked") @Override public int compare(Pair<T1, T2> pair1, Pair<T1, T2> pair2) { return -((Comparable<T1>) pair1.first()).compareTo(pair2.first()); } } /** * Compares a <code>Pair</code> to another <code>Pair</code> according to the second object of the pair only * This function will work providing * the first element of the <code>Pair</code> is comparable, otherwise will throw a * <code>ClassCastException</code> * @author jonathanberant * * @param <T1> * @param <T2> */ public static class BySecondPairComparator<T1,T2> implements Comparator<Pair<T1,T2>> { @SuppressWarnings("unchecked") @Override public int compare(Pair<T1, T2> pair1, Pair<T1, T2> pair2) { return ((Comparable<T2>) pair1.second()).compareTo(pair2.second()); } } /** * Compares a <code>Pair</code> to another <code>Pair</code> according to the second object of the pair only in decreasing order * This function will work providing * the first element of the <code>Pair</code> is comparable, otherwise will throw a * <code>ClassCastException</code> * @author jonathanberant * * @param <T1> * @param <T2> */ public static class BySecondReversePairComparator<T1,T2> implements Comparator<Pair<T1,T2>> { @SuppressWarnings("unchecked") @Override public int compare(Pair<T1, T2> pair1, Pair<T1, T2> pair2) { return -((Comparable<T2>) pair1.second()).compareTo(pair2.second()); } } }