--- /dev/null
+package jalview.util;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A generic immutable pair of values.
+ *
+ * @author mmwarowny
+ *
+ * @param <T>
+ * first value type
+ * @param <U>
+ * second value type
+ */
+public class Pair<T, U> implements Iterable<Object>
+{
+ final T val0;
+
+ final U val1;
+
+ public Pair(T val0, U val1)
+ {
+ this.val0 = val0;
+ this.val1 = val1;
+ }
+
+ /**
+ * Return value at the specified index cast to type R
+ * @param <R> return type
+ * @param index item index
+ * @return value at given index
+ * @throws ClassCastException value cannot be cast to R
+ * @throws IndexOutOfBoundsException index outside tuple size
+ */
+ @SuppressWarnings("unchecked")
+ public <R> R get(int index) throws ClassCastException, IndexOutOfBoundsException
+ {
+ if (index == 0)
+ return (R) val0;
+ if (index == 1)
+ return (R) val1;
+ throw new IndexOutOfBoundsException(index);
+ }
+
+ /**
+ * @return 0th value of the pair
+ */
+ public T get0()
+ {
+ return val0;
+ }
+
+ /**
+ * @return 1st value of the pair
+ */
+ public U get1()
+ {
+ return val1;
+ }
+
+ /**
+ * @return tuple size
+ */
+ public int size()
+ {
+ return 2;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (obj instanceof Pair)
+ {
+ Pair<?, ?> other = (Pair<?, ?>) obj;
+ return Objects.equals(val0, other.val0) &&
+ Objects.equals(val1, other.val1);
+ }
+ return false;
+ }
+
+ @Override
+ public Iterator<Object> iterator()
+ {
+ return List.of(val0, val1).iterator();
+ }
+}