net.yetamine.lang.containers.tuples.Tuple2 Maven / Gradle / Ivy
Show all versions of net.yetamine.lang Show documentation
/*
* Copyright 2016 Yetamine
*
* 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 net.yetamine.lang.containers.tuples;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import net.yetamine.lang.collections.Iterators;
/**
* A rudimentary tuple implementation consisting of two elements.
*
* @param
* the type of element #1
* @param
* the type of element #2
*/
public final class Tuple2 implements Tuple {
/** Common shared empty tuple. */
private static final Tuple2, ?> EMPTY = new Tuple2<>(null, null);
/** Element #1. */
private final T1 value1;
/** Element #2. */
private final T2 value2;
/**
* Creates a new instance.
*
* @param t1
* element #1
* @param t2
* element #2
*/
private Tuple2(T1 t1, T2 t2) {
value1 = t1;
value2 = t2;
}
// Core construction methods
/**
* Creates a new instance.
*
*
* This method is an alias for {@link #of(Object, Object)} and it is meant
* mainly as a support for static imports.
*
* @param
* the type of element #1
* @param
* the type of element #2
* @param t1
* element #1
* @param t2
* element #2
*
* @return the new instance
*/
public static Tuple2 tuple2(T1 t1, T2 t2) {
return of(t1, t2);
}
/**
* Creates a new instance.
*
* @param
* the type of element #1
* @param
* the type of element #2
* @param t1
* element #1
* @param t2
* element #2
*
* @return the new instance
*/
public static Tuple2 of(T1 t1, T2 t2) {
return new Tuple2<>(t1, t2);
}
/**
* Returns an empty tuple (consisting of {@code null} elements).
*
* @param
* the type of element #1
* @param
* the type of element #2
*
* @return an empty tuple
*/
@SuppressWarnings("unchecked")
public static Tuple2 empty() {
return (Tuple2) EMPTY;
}
/**
* Narrows a widened type performing a safe type cast (thanks to the safe
* covariant changes for immutable types).
*
* @param
* the type of element #1
* @param
* the type of element #2
* @param instance
* the instance to narrow
*
* @return the narrowed instance
*/
@SuppressWarnings("unchecked")
public static Tuple2 narrow(Tuple2 extends T1, ? extends T2> instance) {
return (Tuple2) instance;
}
// Common object methods
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return String.format("(%s, %s)", value1, value2);
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof Tuple2, ?>) {
final Tuple2, ?> o = (Tuple2, ?>) obj;
return Objects.equals(value1, o.value1) && Objects.equals(value2, o.value2);
}
return false;
}
/**
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode() {
return Objects.hash(value1, value2);
}
// Inherited methods
/**
* @see net.yetamine.lang.containers.tuples.Tuple#arity()
*/
public int arity() {
return 2;
}
/**
* @see net.yetamine.lang.containers.tuples.Tuple#get(int)
*/
public Object get(int index) {
switch (index) {
case 0:
return get1();
case 1:
return get2();
default:
throw new IndexOutOfBoundsException();
}
}
/**
* @see net.yetamine.lang.containers.tuples.Tuple#toList()
*/
public List> toList() {
return Collections.unmodifiableList(Arrays.asList(value1, value2));
}
// Core tuple methods
/**
* Returns element #1.
*
* @return element #1
*/
public T1 get1() {
return value1;
}
/**
* Returns element #2.
*
* @return element #2
*/
public T2 get2() {
return value2;
}
/**
* Returns a tuple with element #1 modified to the given value.
*
* @param
* the type of the value
* @param value
* the value to set
*
* @return a tuple with element #1 modified to the given value
*/
public Tuple2 set1(V value) {
return of(value, value2);
}
/**
* Returns a tuple with element #2 modified to the given value.
*
* @param
* the type of the value
* @param value
* the value to set
*
* @return a tuple with element #2 modified to the given value
*/
public Tuple2 set2(V value) {
return of(value1, value);
}
/**
* Returns a tuple with swapped elements.
*
* @return a tuple with swapped elements
*/
public Tuple2 swap() {
return (this == EMPTY) ? empty() : of(value2, value1);
}
// Link to Tuple3
/**
* Makes a tuple with the given value prepended to this tuple.
*
* @param
* the type of the value
* @param value
* the value to prepend
*
* @return a tuple with the given value prepended to this tuple
*/
public Tuple3 prepend(V value) {
return Tuple3.of(value, value1, value2);
}
/**
* Makes a tuple with the given value appended to this tuple.
*
* @param
* the type of the value
* @param value
* the value to append
*
* @return a tuple with the given value appended to this tuple
*/
public Tuple3 append(V value) {
return Tuple3.of(value1, value2, value);
}
/**
* Makes a tuple with the given value inserted between the elements of this
* tuple.
*
* @param
* the type of the value
* @param value
* the value to insert
*
* @return a tuple with the given value inserted between the elements of
* this tuple
*/
public Tuple3 insert(V value) {
return Tuple3.of(value1, value, value2);
}
// Functional extensions
/**
* Returns a tuple with element #1 mapped with the given function.
*
* @param
* the type of the function result
* @param mapping
* the function to apply. It must not be {@code null}.
*
* @return a tuple with element #1 mapped with the given function
*/
public Tuple2 map1(Function super T1, ? extends V> mapping) {
return of(mapping.apply(value1), value2);
}
/**
* Returns a tuple with element #2 mapped with the given function.
*
* @param
* the type of the function result
* @param mapping
* the function to apply. It must not be {@code null}.
*
* @return a tuple with element #2 mapped with the given function
*/
public Tuple2 map2(Function super T2, ? extends V> mapping) {
return of(value1, mapping.apply(value2));
}
/**
* Passes element #1 to the specified consumer.
*
* @param consumer
* the consumer to call. It must not be {@code null}.
*
* @return this instance
*/
public Tuple2 use1(Consumer super T1> consumer) {
consumer.accept(value1);
return this;
}
/**
* Passes element #2 to the specified consumer.
*
* @param consumer
* the consumer to call. It must not be {@code null}.
*
* @return this instance
*/
public Tuple2 use2(Consumer super T2> consumer) {
consumer.accept(value2);
return this;
}
/**
* Passes the elements of this tuple to the given {@link BiConsumer}.
*
* @param consumer
* the consumer to apply on the elements. It must not be
* {@code null}.
*
* @return this instance
*/
public Tuple2 use(BiConsumer super T1, ? super T2> consumer) {
consumer.accept(value1, value2);
return this;
}
/**
* Applies the given function on the elements of this tuple and returns its
* result.
*
* @param
* the type of the result
* @param mapping
* the function to apply on the elements. It must not be
* {@code null}.
*
* @return the result of the given function
*/
public V map(BiFunction super T1, ? super T2, ? extends V> mapping) {
return mapping.apply(value1, value2);
}
// Factory methods for common types
/**
* Converts an {@link java.util.Map.Entry} instance into a tuple.
*
* @param
* the type of the entry's key
* @param
* the type of the entry's value
* @param entry
* the entry to convert. It must not be {@code null}.
*
* @return a tuple containing the entry content
*/
public static Tuple2 from(Map.Entry extends K, ? extends V> entry) {
return of(entry.getKey(), entry.getValue());
}
/**
* Makes a tuple from the first two elements provided by the given source.
*
* @param
* the type of the source's elements
* @param source
* the source to process. It must provide at least two elements.
*
* @return a tuple
*
* @throws NoSuchElementException
* if the source provides too few elements
*/
public static Tuple2 from(Iterable extends T> source) {
return from(source.iterator());
}
/**
* Makes a tuple from the first two elements provided by the given source.
*
* @param
* the type of the source's elements
* @param source
* the source to process. It must provide at least two elements.
*
* @return a tuple
*
* @throws NoSuchElementException
* if the source provides too few elements
*/
public static Tuple2 from(Iterator extends T> source) {
return of(source.next(), source.next());
}
// Zipping
/**
* Returns an iterable zipping the elements from given source iterables.
*
*
* The resulting iterables returns tuples from the elements provided by the
* source iterables and returns as many elements as the shorter of the
* source iterables.
*
* @param
* the type of element #1
* @param
* the type of element #2
* @param source1
* the source of elements. It must not be {@code null}.
* @param source2
* the source of elements. It must not be {@code null}.
*
* @return a zipping iterable
*/
public static Iterable> zip(Iterable extends T1> source1, Iterable extends T2> source2) {
Objects.requireNonNull(source1);
Objects.requireNonNull(source2);
// Rather not make a lambda, it might change
return new Iterable>() {
/**
* @see java.lang.Iterable#iterator()
*/
public Iterator> iterator() {
return Tuple2.zip(source1.iterator(), source2.iterator());
}
};
}
/**
* Returns an iterator zipping the elements from given source iterators.
*
*
* The resulting iterator returns tuples from the elements provided by the
* source iterators and returns as many elements as the shorter of the
* source iterators.
*
* @param
* the type of element #1
* @param
* the type of element #2
* @param source1
* the source of elements. It must not be {@code null}.
* @param source2
* the source of elements. It must not be {@code null}.
*
* @return a zipping iterator
*/
public static Iterator> zip(Iterator extends T1> source1, Iterator extends T2> source2) {
Objects.requireNonNull(source1);
Objects.requireNonNull(source2);
return new Iterator>() {
/**
* @see java.util.Iterator#hasNext()
*/
public boolean hasNext() {
return source1.hasNext() && source2.hasNext();
}
/**
* @see java.util.Iterator#next()
*/
public Tuple2 next() {
return Tuple2.of(source1.next(), source2.next());
}
};
}
/**
* Returns a stream zipping the elements from given source streams.
*
*
* The resulting stream returns tuples from the elements provided by the
* source streams and returns as many elements as the shorter of the source
* streams.
*
* @param
* the type of element #1
* @param
* the type of element #2
* @param source1
* the source of elements. It must not be {@code null}.
* @param source2
* the source of elements. It must not be {@code null}.
*
* @return a zipping stream
*/
public static Stream> zip(Stream extends T1> source1, Stream extends T2> source2) {
return Iterators.stream(zip(source1.iterator(), source2.iterator()));
}
// Link to Map
/**
* Returns this instance as an immutable {@link Map}.
*
* @return this instance as an immutable {@link Map}
*/
public Map asMap() {
return Collections.singletonMap(value1, value2);
}
/**
* Provides the default {@link Collector} to collect a stream of tuples to a
* map.
*
*
* This method is just a convenient shortcut for getting the suitable
* collector using {@link Collectors#toMap(Function, Function)}.
*
* @param
* the type of element #1
* @param
* the type of element #2
*
* @return a new collector
*/
public static Collector, ?, Map> toMap() {
return Collectors.toMap(Tuple2::get1, Tuple2::get2);
}
}