com.healthmarketscience.common.util.Tuple2 Maven / Gradle / Ivy
The newest version!
/*
Copyright (c) 2007 Health Market Science, Inc.
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 com.healthmarketscience.common.util;
import java.io.Serializable;
import java.util.AbstractCollection;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.builder.EqualsBuilder;
/**
* Simple class for holding a pair of typed objects. Also implements
* {@code Entry.Map}. You can construct a {@code Tuple2} from a
* {@code Map.Entry}, and {@link #equals} and {@link #hashCode} are both
* special-cased to handle {@code Map.Entry}.
*
* @author James Ahlborn
*/
public class Tuple2
extends Tuple1
implements Cloneable, Serializable, Map.Entry
{
private static final long serialVersionUID = 20071230;
/**
* Helper array to get direct indexed access to the tuple values.
*/
static final Accessor[] ACCESSORS2 = {
ACCESSORS1[0],
new Accessor() {
@Override
public Object get(Tuple1> tuple) {
return ((Tuple2,?>)tuple).get1();
}
}
};
/**
* The second object to store in this tuple.
*/
private ObjType1 _obj1;
/**
* Initializes a new Tuple2 with null objects.
*/
public Tuple2() {
this(null, null);
}
/**
* Initializes a new Tuple2 with the given objects. You may want to use
* {@link #create} instead, since it infers the type parameters based on
* its arguments.
*
* @param obj0 the first object to store in this tuple.
* @param obj1 the second object to store in this tuple.
*/
public Tuple2(ObjType0 obj0, ObjType1 obj1) {
super(obj0);
_obj1 = obj1;
}
/**
* Initializes a new Tuple2 based on the given Map.Entry.
*
* @param entry the entry base this Tuple2 on
*/
public Tuple2(Map.Entry extends ObjType0, ? extends ObjType1> entry) {
this(entry.getKey(), entry.getValue());
}
/**
* Creates a new Tuple2 with the provided objects
*
* @param obj0 the first object to store in this tuple.
* @param obj1 the second object to store in this tuple.
* @return a new Tuple2 that contains the provided objects
*/
public static Tuple2 create(
InObjType0 obj0, InObjType1 obj1)
{
return new Tuple2(obj0, obj1);
}
/**
* Creates a new Tuple2 with the Map.Entry's key and value as the objects.
*
* @param entry the Map.Entry from which a new Tuple2 will be created.
* @return a new Tuple2 whose first object is the given Map.Entry's key,
* and whose second object is the Map.Entry's value.
*/
public static Tuple2 create(
Map.Entry entry)
{
return new Tuple2(entry);
}
/**
* Get an Iterable adapter that returns an Iterator that iterates over
* element 1 of the Tuples within the given Iterable/Iterator.
*
* @return an Iterable adapter that returns an Iterator that iterates over
* element 1 of the Tuples within the given Iterable/Iterator.
*/
public static Iterable iterable1(
final Iterable extends Tuple2, InObjType1>> iable)
{
return new IterableIterator()
{
private final Iterator extends Tuple2, InObjType1>> _iter =
iable.iterator();
public boolean hasNext() {
return _iter.hasNext();
}
public InObjType1 next() {
return _iter.next().get1();
}
public void remove() {
_iter.remove();
}
};
}
/**
* Get a Collection adapter for element 1 of the Tuples of the given
* Collection. The returned Collection supports element removal if the given
* Collection supports element removal, but does not support element
* addition.
*
* @return a Collection adapter for element 1 of the Tuples of the given
* Collection.
*/
public static Collection collection1(
final Collection extends Tuple2, InObjType1>> col)
{
return new AbstractCollection()
{
private final Collection extends Tuple2, InObjType1>> _col = col;
@Override
public Iterator iterator() {
return iterable1(_col).iterator();
}
@Override
public int size() { return _col.size(); }
};
}
/**
* Returns the second object of this tuple.
*
* @return the second object of this tuple.
*/
public final ObjType1 get1() { return _obj1; }
/**
* Sets the second object of this tuple.
*
* @param obj the object to set as the second object of this tuple.
*/
public final void set1(ObjType1 obj) { _obj1 = obj; }
/**
* Get the objects of this tuple as an array.
*
* @return the objects of this tuple as an array.
*/
@Override
public Object[] get() { return new Object[]{get0(), get1()}; }
/**
* Get the value of this tuple identified by index, which must always be
* either 0 or 1, since this tuple class stores exactly two objects.
*
* @param index the index of the object to retrieve. Must be 0 or 1.
* @return the object in this tuple with the given index.
*/
@Override
public Object get(int index) { return ACCESSORS2[index].get(this); }
/**
* Sets the values of this Tuple2.
*
* @param obj0 the object to use as the first object of this tuple.
* @param obj1 the object to use as the second object of this tuple.
*/
public final void set(ObjType0 obj0, ObjType1 obj1) {
set0(obj0);
set1(obj1);
}
/**
* Get the first object of this tuple.
*
* @return the first object of this tuple.
*/
public ObjType0 getKey() {
return get0();
}
/**
* Get the second object of this tuple.
*
* @return the second object of this tuple.
*/
public ObjType1 getValue() {
return get1();
}
/**
* Set the second object of this tuple.
*
* @param newValue the object to set as the second object of this tuple.
* @return the object that was previously the second value of this tuple.
*/
public ObjType1 setValue(ObjType1 newValue) {
ObjType1 oldValue = get1();
set1(newValue);
return oldValue;
}
/**
* Get the size of this tuple, which is always 2.
*
* @return 2
*/
@Override
public int size() { return 2; }
/**
* Makes a copy of this Tuple2.
*
* @return a copy of this Tuple2.
*/
@SuppressWarnings({"unchecked","PMD.CloneThrowsCloneNotSupportedException"})
@Override
public Tuple2 clone() {
return (Tuple2)super.clone();
}
/**
* Indicates whether the given Object is equal to this Tuple2.
* @return
* {@code true} if the given Object refers to this Tuple2.
* {@code true} if the given Object is an instance of Tuple2 for which
* the contained objects are equal to this Tuple2's objects.
* {@code true} if the given Object is an instance of Entry.Map, and
* the Object's key and value are equal to this Tuple2's key and value,
* respectively.
* {@code false} otherwise.
*/
@Override
public boolean equals(Object o)
{
if(this == o) {
return true;
}
if((o == null) || (o.getClass() != getClass())) {
// special case Map.Entry handling
if((o instanceof Map.Entry) && !(o instanceof Tuple2)) {
return new EqualsBuilder()
.append(getKey(), ((Map.Entry,?>)o).getKey())
.append(getValue(), ((Map.Entry,?>)o).getValue())
.isEquals();
}
return false;
}
return new EqualsBuilder()
.append(get0(), ((Tuple2)o).get0())
.append(get1(), ((Tuple2)o).get1())
.isEquals();
}
/**
* Returns a hash code for this Tuple2, based on the objects it contains.
*
* @return a hash code for this Tuple2.
*/
@Override
public int hashCode()
{
// use hashCode compatible with Map.Entry
return ObjectUtils.hashCode(getKey()) ^
ObjectUtils.hashCode(getValue());
}
}