All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.d2ab.util.Pair Maven / Gradle / Ivy

There is a newer version: 2.3.0
Show newest version
/*
 * Copyright 2016 Daniel Skogquist Åborg
 *
 * Licensed 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.d2ab.util;

import org.d2ab.function.Functions;
import org.d2ab.function.QuaternaryFunction;

import java.util.Comparator;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.function.*;

import static java.util.Comparator.comparing;
import static org.d2ab.util.Comparators.naturalOrderNullsFirst;

/**
 * A general purpose pair of two objects, "{@code left}" and "{@code right}". Pairs implement {@link Map.Entry} where
 * the key is the "left" side and the value is the "right" side.
 *
 * @param  the type of the "left" side of the pair.
 * @param  the type of the "right" side of the pair.
 */
public interface Pair extends Entry, Comparable> {
	/**
	 * @return a {@code Pair} of the two objects given.
	 */
	static  Pair of(L left, R right) {
		return new Base() {
			@Override
			public L getLeft() {
				return left;
			}

			@Override
			public R getRight() {
				return right;
			}
		};
	}

	/**
	 * @return a {@code Pair} that delegates to the given {@link Map.Entry}, key being left and value being right.
	 */
	static  Pair from(Entry entry) {
		return new Base() {
			@Override
			public K getLeft() {
				return entry.getKey();
			}

			@Override
			public V getRight() {
				return entry.getValue();
			}
		};
	}

	/**
	 * @return a unary {@code Pair} where both objects are the same.
	 */
	static  Pair unary(T item) {
		return new Base() {
			@Override
			public T getLeft() {
				return item;
			}

			@Override
			public T getRight() {
				return item;
			}
		};
	}

	static  UnaryOperator> asUnaryOperator(BiFunction>
			                                                        op) {
		return entry -> op.apply(entry.getLeft(), entry.getRight());
	}

	static  UnaryOperator> asUnaryOperator(BiFunction> f, BiFunction> g) {

		Function, ? extends Pair> f1 = asFunction(f);
		Function, ? extends Pair> g1 = asFunction(g);
		return Functions.toUnaryOperator(f1, g1);
	}

	static  BinaryOperator> asBinaryOperator(QuaternaryFunction> f) {
		return (e1, e2) -> f.apply(e1.getLeft(), e1.getRight(), e2.getLeft(), e2.getRight());
	}

	static  Function, ? extends R> asFunction(BiFunction mapper) {
		return entry -> mapper.apply(entry.getLeft(), entry.getRight());
	}

	static  Predicate> asPredicate(BiPredicate predicate) {
		return entry -> predicate.test(entry.getLeft(), entry.getRight());
	}

	static  Consumer> asConsumer(BiConsumer action) {
		return entry -> action.accept(entry.getLeft(), entry.getRight());
	}

	/**
	 * @return the "left" component of the {@code Pair}.
	 */
	L getLeft();

	/**
	 * @return the "right" component of the {@code Pair}.
	 */
	R getRight();

	/**
	 * @return the "left" component of the {@code Pair}.
	 */
	@Override
	default L getKey() {
		return getLeft();
	}

	/**
	 * @return the "right" component of the {@code Pair}.
	 */
	@Override
	default R getValue() {
		return getRight();
	}

	/**
	 * This operation is not supported and throws {@link UnsupportedOperationException}.
	 */
	@Override
	default R setValue(R value) {
		throw new UnsupportedOperationException();
	}

	/**
	 * @return a {@code Pair} with the "left" and "right" components of this {@code Pair} swapped.
	 */
	default Pair swap() {
		return new Base() {
			@Override
			public R getLeft() {
				return Pair.this.getRight();
			}

			@Override
			public L getRight() {
				return Pair.this.getLeft();
			}
		};
	}

	/**
	 * @return a {@code Pair} where the "left" component is replaced with the given value.
	 */
	default  Pair withLeft(LL left) {
		return new Base() {
			@Override
			public LL getLeft() {
				return left;
			}

			@Override
			public R getRight() {
				return Pair.this.getRight();
			}
		};
	}

	/**
	 * @return a {@code Pair} where the "right" component is replaced with the given value.
	 */
	default  Pair withRight(RR right) {
		return new Base() {
			@Override
			public L getLeft() {
				return Pair.this.getLeft();
			}

			@Override
			public RR getRight() {
				return right;
			}
		};
	}

	/**
	 * @return a {@code Pair} where the "left" component is shifted to the "right" component and replaced with the
	 * given value.
	 */
	default  Pair shiftRight(LL replacement) {
		return new Base() {
			@Override
			public LL getLeft() {
				return replacement;
			}

			@Override
			public L getRight() {
				return Pair.this.getLeft();
			}
		};
	}

	/**
	 * @return a {@code Pair} where the "right" component is shifted to the "left" component and replaced with the
	 * given value.
	 */
	default  Pair shiftLeft(RR replacement) {
		return new Base() {
			@Override
			public R getLeft() {
				return Pair.this.getRight();
			}

			@Override
			public RR getRight() {
				return replacement;
			}
		};
	}

	/**
	 * @return a {@code Pair} mapped from this {@code Pair} using the given mappers.
	 */
	default  Pair map(Function leftMapper,
	                                  Function rightMapper) {
		return new Base() {
			@Override
			public LL getLeft() {
				return leftMapper.apply(Pair.this.getLeft());
			}

			@Override
			public RR getRight() {
				return rightMapper.apply(Pair.this.getRight());
			}
		};
	}

	/**
	 * @return a {@code Pair} mapped from this {@code Pair} using the given mapper.
	 */
	default  Pair map(BiFunction> mapper) {
		return mapper.apply(getLeft(), getRight());
	}

	/**
	 * @return a the result of applying the given {@link BiFunction} to the "left" and "right" components of this
	 * {@code Pair}.
	 */
	default  T apply(BiFunction function) {
		return function.apply(getLeft(), getRight());
	}

	/**
	 * @return the result of testing the given {@link Predicate}s on the "left" and "right" components of this
	 * {@code Pair}.
	 */
	default boolean test(Predicate leftPredicate, Predicate rightPredicate) {
		return leftPredicate.test(getLeft()) && rightPredicate.test(getRight());
	}

	/**
	 * @return the result of testing the given {@link BiPredicate}s on the "left" and "right" components of this
	 * {@code Pair}.
	 */
	default boolean test(BiPredicate predicate) {
		return predicate.test(getLeft(), getRight());
	}

	default Map put(Map map) {
		map.put(getLeft(), getRight());
		return map;
	}

	/**
	 * @return an iterator over the components of this {@code Pair}, containing exactly two elements.
	 */
	default  Iterator iterator() {
		@SuppressWarnings("unchecked")
		PairIterator pairIterator = new PairIterator(this);
		return pairIterator;
	}

	abstract class Base implements Pair {
		@SuppressWarnings("unchecked")
		private static final Comparator COMPARATOR =
				comparing((Function) Pair::getLeft, naturalOrderNullsFirst()).thenComparing(
						(Function) Pair::getRight, naturalOrderNullsFirst());

		public static String format(Object o) {
			if (o instanceof String) {
				return '"' + (String) o + '"';
			}
			return String.valueOf(o);
		}

		@Override
		public int hashCode() {
			int result = (getLeft() != null) ? getLeft().hashCode() : 0;
			result = (31 * result) + ((getRight() != null) ? getRight().hashCode() : 0);
			return result;
		}

		@Override
		public boolean equals(Object o) {
			if (this == o)
				return true;
			if (!(o instanceof Pair))
				return false;

			Pair that = (Pair) o;

			return ((getLeft() != null) ? getLeft().equals(that.getLeft()) : (that.getLeft() == null)) &&
			       ((getRight() != null) ? getRight().equals(that.getRight()) : (that.getRight() == null));
		}

		@Override
		public String toString() {
			return "(" + format(getLeft()) + ", " + format(getRight()) + ')';
		}

		@Override
		public int compareTo(Pair that) {
			return COMPARATOR.compare(this, that);
		}
	}

	class PairIterator implements Iterator {
		private final Pair pair;
		int index;

		public PairIterator(Pair pair) {
			this.pair = pair;
		}

		@Override
		public boolean hasNext() {
			return index < 2;
		}

		@Override
		public T next() {
			if (!hasNext())
				throw new NoSuchElementException();

			switch (++index) {
				case 1:
					return pair.getLeft();
				case 2:
					return pair.getRight();
				default:
					// Can't happen due to above check
					throw new IllegalStateException();
			}
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy