package org.threadly.util; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * A simple tuple implementation (every library needs one, right?). This is designed to be a * minimal and light weight pair holder. * * @since 4.4.0 * @param <L> Type of 'left' object to be held * @param <R> Type of 'right' object to be held */ public class Pair<L, R> { private static final short LEFT_PRIME = 13; private static final short RIGHT_PRIME = 31; // TODO - in threadly 5.0.0 make an easy way to apply a functional to either left or right /** * Split a collection of pair's into a pair of two collections. This is more efficient than * invoking {@link #getLeft()} and {@link #getRight()} separately. Similar to those functions, * this will only collect non-null entries. If there is a {@code null} left or right entry then * the indexes will no longer match each other between the two lists. * * @param <L> Type of object held as pair's left reference * @param <R> Type of object held as pair's right reference * @param source Source collection of pairs * @return Pair of two collections with the left and right halves */ public static <L, R> Pair<List<L>, List<R>> split(Collection<? extends Pair<? extends L, ? extends R>> source) { int estimatedCount = source.size(); List<L> left = new ArrayList<>(estimatedCount); List<R> right = new ArrayList<>(estimatedCount); for (Pair<? extends L, ? extends R> p : source) { if (p.left != null) { left.add(p.left); } if (p.right != null) { right.add(p.right); } } return new Pair<>(left, right); } /** * Collect all the non-null left references into a new List. A simple implementation which * iterates over a source collection and collects all non-null left references into a new list * that can be manipulated or referenced. * * @param <T> Type of object held as pair's left reference * @param source Source collection of pairs * @return New list that contains non-null left references */ public static <T> List<T> collectLeft(Collection<? extends Pair<? extends T, ?>> source) { List<T> result = new ArrayList<>(source.size()); for (Pair<? extends T, ?> p : source) { if (p.left != null) { result.add(p.left); } } return result; } /** * Collect all the non-null right references into a new List. A simple implementation which * iterates over a source collection and collects all non-null right references into a new list * that can be manipulated or referenced. * * @param <T> Type of object held as pair's right reference * @param source Source collection of pairs * @return New list that contains non-null right references */ public static <T> List<T> collectRight(Collection<? extends Pair<?, ? extends T>> source) { List<T> result = new ArrayList<>(source.size()); for (Pair<?, ? extends T> p : source) { if (p.right != null) { result.add(p.right); } } return result; } /** * Simple search to see if a collection of pairs contains a given left value. It is assumed * that the iterator will not return any {@code null} elements. * * @param search Iterable to search over * @param value Value to be searching for from left elements * @return {@code true} if the value is found as a left element from the iterable provided */ public static boolean containsLeft(Iterable<? extends Pair<?, ?>> search, Object value) { for (Pair<?, ?> p : search) { if (p.left == null) { if (value == null) { return true; } } else if (p.left.equals(value)) { return true; } } return false; } /** * Simple search to see if a collection of pairs contains a given right value. It is assumed * that the iterator will not return any {@code null} elements. * * @param search Iterable to search over * @param value Value to be searching for from right elements * @return {@code true} if the value is found as a right element from the iterable provided */ public static boolean containsRight(Iterable<? extends Pair<?, ?>> search, Object value) { for (Pair<?, ?> p : search) { if (p.right == null) { if (value == null) { return true; } } else if (p.right.equals(value)) { return true; } } return false; } /** * Get the right side of a pair by searching for a matching left side. This iterates over the * provided source, and once the first pair with a left that matches (via * {@link Object#equals(Object)}), the right side is returned. If no match is found, * {@code null} will be returned. Although the implementer must be aware that since nulls can * be kept kept inside pairs, that does not strictly indicate a match failure. * * @param <T> Type of object held as pair's right reference * @param search Iteratable to search through looking for a match * @param left Object to be looking searching for as a left reference * @return Corresponding right reference or {@code null} if none is found */ public static <T> T getRightFromLeft(Iterable<? extends Pair<?, ? extends T>> search, Object left) { for (Pair<?, ? extends T> p : search) { if (p.left == null) { if (left == null) { return p.right; } } else if (p.left.equals(left)) { return p.right; } } return null; } /** * Get the left side of a pair by searching for a matching right side. This iterates over the * provided source, and once the first pair with a right that matches (via * {@link Object#equals(Object)}), the left side is returned. If no match is found, * {@code null} will be returned. Although the implementer must be aware that since nulls can * be kept kept inside pairs, that does not strictly indicate a match failure. * * @param <T> Type of object held as pair's left reference * @param search Iteratable to search through looking for a match * @param right Object to be looking searching for as a left reference * @return Corresponding left reference or {@code null} if none is found */ public static <T> T getLeftFromRight(Iterable<? extends Pair<? extends T, ?>> search, Object right) { for (Pair<? extends T, ?> p : search) { if (p.right == null) { if (right == null) { return p.left; } } else if (p.right.equals(right)) { return p.left; } } return null; } // not final so extending classes can mutate protected L left; protected R right; /** * Constructs a new pair, providing the left and right objects to be held. * * @param left Left reference * @param right Right reference */ public Pair(L left, R right) { this.left = left; this.right = right; } /** * Getter to get the left reference stored in the pair. * * @return Left reference */ public L getLeft() { return left; } /** * Getter to get the right reference stored in the pair. * * @return Right reference */ public R getRight() { return right; } @Override public String toString() { return Pair.class.getSimpleName() + '[' + left + ',' + right + ']'; } @Override public boolean equals(Object o) { if (this == o) { return true; } else if (o instanceof Pair) { Pair<?, ?> p = (Pair<?, ?>)o; if (! (left == p.left || (left != null && left.equals(p.left)))) { return false; } else if (! (right == p.right || (right != null && right.equals(p.right)))) { return false; } else { return true; } } else { return false; } } @Override public int hashCode() { int leftHash = left == null ? LEFT_PRIME : left.hashCode(); int rightHash = right == null ? RIGHT_PRIME : right.hashCode(); return leftHash ^ rightHash; } }