Using Pairs or 2-tuples in Java [duplicate]
Asked Answered
B

14

363

My Hashtable in Java would benefit from a value having a tuple structure. What data structure can I use in Java to do that?

Hashtable<Long, Tuple<Set<Long>,Set<Long>>> table = ...
Beacham answered 19/4, 2010 at 21:20 Comment(9)
You mean a pair, i.e. a tuple of length 2? In any case, I guess your best bet is to write a class of your own, shouldn't be too hard.Broadbent
@Alexander, what if this particular Map needs safe concurrent access, then wouldn't Hashtable be the right choice?Stringency
I'd say wrapping with Collections.synchronizedMap(Map) would be preferable.Langille
If concurrent access is needed, then ConcurrentHashMap is the right class to use. It's more expressive and has better performance than a synchronized HashMap or Hashtable.Taunt
Maybe I've just seen too many Hashtables used when a HashMap should have been used... Collections.synchronizedMap at least signals that your intent is to synchronize. Though yeah, ConcurrentHashMap is even better.Langille
couldn't you just use AbstractMap.SimpleEntry<K,V>, i have used this before when i want to achieve the Pair<X,Y> like behavior, out of the box. docs.oracle.com/javase/6/docs/api/java/util/…Bloomsbury
This is not asking for a tuple, this is a pair, or 2-tuple - as @doublep pointed out. Tuple should be an ordered list of n elements where n is a non-negative integer. (Also, tuples are usually immutable.)Conjunctivitis
Could you edit the title so it's clear if it actually asks about Pairs, not tuples? A tuple is expected to be of n-length. The answers generally address the pair problem. They don't try to provide solutions for actual 'tuple', ie. an n-tuple. Because only Pair is needed to answer the question. And thus this way the question's title overlaps with an potential inquery about n-tuples in general.Conjunctivitis
can't you use the new java-14 Record type?Aesthesia
S
364

I don't think there is a general purpose tuple class in Java but a custom one might be as easy as the following:

public class Tuple<X, Y> { 
  public final X x; 
  public final Y y; 
  public Tuple(X x, Y y) { 
    this.x = x; 
    this.y = y; 
  } 
} 

Of course, there are some important implications of how to design this class further regarding equality, immutability, etc., especially if you plan to use instances as keys for hashing.

Stringency answered 19/4, 2010 at 21:29 Comment(11)
I think it would be better to declare x and y as public final and get rid of those getters.Lintwhite
That is not a tuple. It only hold pairs (tuples of length 2).Bogor
I've used tuples a lot in C# and the documentation is really good if you would want to write a bigger one for your own: msdn.microsoft.com/de-de/library/vstudio/dd387036.aspxDismember
-1 (sorry). as @Bogor told, this is not a tuple - it is a 2-tuple or a pair. Tuples can be created with arbitrary number of elements. -- Your definition may be depending on where do you came from, I guess.Conjunctivitis
This doesn't work. When used in Hashtable or HashMap (as requested) it fails since 2 different tuples with the same elements will provide different hash codes. equals() and hashCode() must be overriden.Dyeing
@YagoMéndezVidal: yes, the last sentence of my answer says "there are some important implications of how to design this class further regarding equality, immutability, etc., especially if you plan to use instances as keys for hashing."Stringency
@aem999 - The question you link to has been deleted.Pickax
@Pickax updated link https://mcmap.net/q/93686/-java-n-tuple-implementationEngrossment
@Pickax I don't have sufficient privilege to update the link in the comment - the link has changed to: https://mcmap.net/q/93686/-java-n-tuple-implementationRuyle
Java 9+ : Entry<Integer, String> pair = Map.entry(1, "a");Ventura
In JDK 14 onwards, you can use records to implement tuples. For example, public record Pair<X, Y>(X x, Y y) {}. You get tuple semantics for equality checks and hash code calculations out of the box.Galleass
R
184

javatuples is a dedicated project for tuples in Java.

Unit<A> (1 element)
Pair<A,B> (2 elements)
Triplet<A,B,C> (3 elements)
Rocketeer answered 17/5, 2012 at 7:41 Comment(4)
Does it only cover tuples to 10-tuples?Conjunctivitis
@n611x007, I think every tuple variant is hand-written (i.e. you can't write java code with a variable number of type parameters, at least not easily), and so it makes sense for the library writer to stop somewhere, 10 seems like a reasonable number to support most use-cases.Gracielagracile
Some of the striking characteristics of the javatuple library as described in their website -- All tuple classes are: Typesafe, Immutable, Iterable, Serializable, Comparable (implements Comparable<Tuple>), Implementing equals(...) and hashCode(), and Implementing toString(). Might be useful to know.Hartzog
Shame about their naming, especially the (unnecessary) Unit, which has different semantics from the Unit in Kotlin, Scala etc.Steam
N
120

Apache Commons provided some common java utilities including a Pair. It implements Map.Entry, Comparable and Serializable.

Nakesha answered 1/3, 2013 at 14:10 Comment(7)
The implementation provided by Apache Commons fulfills exactly what I needed. I am happy to have a working implementation.Serbocroatian
Well, the question seems to be wrong about tuples. Pairs != Tuples. Tuples are n-length, ie. any length.Conjunctivitis
@naxa please re-check the question description and you may found a Pair is exactly what he need.Nakesha
you are right in that and I may did wrong with pasting my view as comment in some answers, including yours. Unfortunatelly I tend to search questions based on their title; it's unclearness on the question's part that it uses a way more general title than the asker's actual problem.Conjunctivitis
@naxa: In a language where generic types are distinguishable by the number of parameters, it's fine to types called Tuple<T1,T2>, Tuple<T1,T2,T3>, etc. The names are not good in Java, which does not allow such distinction, but that doesn't mean Tuple is not a good name for classes with hardcoded number of types in languages with "real" generic types.Pegram
Note for Android developers - Android SDK has already implement genric Pair class which supported from either API 5 or API 4 (support library).Cob
I was excited to see that the Pair class in Apache Commons is in the org.apache.commons.lang3.tuple package, just to find out that a pair is the only type of tuple implemented :(Orlon
M
74

If you are looking for a built-in Java two-element tuple, try AbstractMap.SimpleEntry.

Malocclusion answered 25/2, 2013 at 16:54 Comment(7)
Less readable. If I saw this in code I'd wonder where the map is.Lateshalatest
While the intended purpose of this class is to facilitate the creation of custom map implementation according to the javadoc, this gets the job done nicely. +1. Also, readability isn't that big of an issue in this case.Pebrook
If readability is a concern you could also create your own tuple like this: class Tuple extends AbstractMap.SimpleEntry {} Then you can refer to the class as Tuple.Jelsma
@Jelsma if you're going through the trouble of creating your own class, why extend AbstractMap.SimpleEntry? Create your own type with exactly the semantics you need and your callers will expect.Cupcake
@dimo414: Because SimpleEntry already implements equals, hashcode and has everything else one needs. If you want a generic Tuple then key and value maybe good names and the result might already be perfectly what you need. Of course if this is not the case then of course you are right that a custom implementation is a good idea.Jelsma
This class is not immutable. Value (one of the elements of tuple) may be changed by setValue() method.Remind
Furthermore accessing the elements of the tuple has to be done using getKey and getValue which is not really what you mean when you want a simple two element tuple.Wheat
A
41

As an extension to @maerics nice answer, I've added a few useful methods:

public class Tuple<X, Y> { 
    public final X x; 
    public final Y y; 
    public Tuple(X x, Y y) { 
        this.x = x; 
        this.y = y; 
    }

    @Override
    public String toString() {
        return "(" + x + "," + y + ")";
    }

    @Override
    public boolean equals(Object other) {
        if (other == this) {
            return true;
        }

        if (!(other instanceof Tuple)){
            return false;
        }

        Tuple<X,Y> other_ = (Tuple<X,Y>) other;

        // this may cause NPE if nulls are valid values for x or y. The logic may be improved to handle nulls properly, if needed.
        return other_.x.equals(this.x) && other_.y.equals(this.y);
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((x == null) ? 0 : x.hashCode());
        result = prime * result + ((y == null) ? 0 : y.hashCode());
        return result;
    }
}
Acetaldehyde answered 8/9, 2012 at 7:16 Comment(6)
If x or y is String, equals method will be not be working ok.Amaty
This is the actual solution as the one accepted will fail in the Hashtable (as requested in the question) since the hasCode() and equals() of 2 same couples will differ.Dyeing
As athabaska noted, just substitute the last line of equals() to return Objects.equals(other_.x == this.x) && Objects.equals(other_.y == this.y) in Java 7 or an equalily comparson with null check.Dyeing
@YagoMéndezVidal good point, this was generated in Eclipse...Acetaldehyde
@YagoMéndezVidal, you mean Objects.equals(other.x, this.x), etc? The method accepts two Object references as parameters, not a boolean value.Swetiana
Yes, of course, @VictorZamanian, you're very right.Dyeing
L
31

Another 2 cents : Starting with Java 7, there is now a class for this in standard Lib : javafx.util.Pair.

And Yes, It is standard Java, now that JavaFx is included in the JDK :)

Lavalava answered 1/7, 2014 at 7:17 Comment(7)
What about 3-tuples?Harkey
a Pair with the value being a Pair should do the trick, no ?Lavalava
That's an ugly hack. So are you going to have a pair in a pair in a pair for 4-tuples?Harkey
I think that if you need a 4-Tuples, it is a good thing to start considerate using a table instead or your own classe...Lavalava
Aaaand it will be gone with jigsaw in JDK 9. I mean you still can use it, but do you really want to include javafx into your project because of this one class? :)Ictinus
Here I am still hoping that the "utility" part of javafx (Properties, Pair, etc.) will be migrated to the core of java, or at least to a specific moduleLavalava
Sadly javafx will soon be removed from the JRE again.Punchinello
A
18

With lombok it's easy to declare a Pair class:

@Data(staticConstructor = "of")
public class Pair<A, B> {
    private final A left;
    private final B right;
}

This will generates getters, static constructor named "of", equals(), hashcode() and toString().

see @Data documentation for more information

Aubigny answered 15/2, 2015 at 12:30 Comment(4)
How does Iombok do this if the two fields A and B are final?Deckard
check the sources on github, I personally don't know the internals. final here ensure we get a static constructor with "left" and "right" arguments and no settersAubigny
@LucasCrawford the fields are final, but the class isn't. You need the class to be extendable to do things like proxying. Also, finals carry only so much weight when reflection is considered (see Class.setAccessible(boolean)).Grangerize
@user180100, re "with lombok it's easy to"; But is it easy to Lombok?Harkey
G
17

Here's this exact same question elsewhere, that includes a more robust equals, hash that maerics alludes to:

http://groups.google.com/group/comp.lang.java.help/browse_thread/thread/f8b63fc645c1b487/1d94be050cfc249b

That discussion goes on to mirror the maerics vs ColinD approaches of "should I re-use a class Tuple with an unspecific name, or make a new class with specific names each time I encounter this situation". Years ago I was in the latter camp; I've evolved into supporting the former.

Galvanize answered 19/4, 2010 at 23:14 Comment(3)
could you paste the relevant part here, before groups close down or anything? SO encourages copying relevant information to on-site.Conjunctivitis
If a method is supposed to examine a double[] and compute the minimum, maximum, average, and standard deviation, having it store the values into a passed-in double[4] may be less elegant than having it use a type with named fields, but someone who wants to know what's going on will only have to read the documentation for the method, instead of having to also read the documentation for a custom return type (or examine the code of it directly). Being able to return anonymous value types would be nicer, but Java doesn't support anything like that. Actually, if I had control over Java...Pegram
...I'd add a few fields to Thread named tempLong1, tempLong2, tempObject1, TempObject2, etc. to minimize the number of temporary objects that need to be created purely for the purpose of being able to return more than one thing from a method. Icky, but there is no other clean solution.Pegram
P
7

Android Tuple Utils

This object provides a sensible implementation of equals(), returning true if equals() is true on each of the contained objects.

Particolored answered 23/1, 2014 at 9:11 Comment(0)
S
7

Though the article is pretty old now, and though I understand that I'm not really very helpful, I think the proposal described in Adding tuples to Java: a study in lightweight data structures, would have been nice in mainstream Java.

You can do things like:

int a;
char b;
float c;
[a,b,c] = [3,'a',2.33];

or

[int,int,char] x = [1,2,'a'];

or

public [int,boolean] Find(int i)
{
  int idx = FindInArray(A,i);
  return [idx,idx>=0];
}

[idx, found] = Find(7);

Here tuples are:

  • Defined as primitive types - no templates/generics
  • Stack-allocated if declared locally
  • Assigned using pattern-matching

This approach increases

  • Performance
  • Readability
  • Expressiveness
Stutzman answered 15/2, 2015 at 12:2 Comment(4)
Has there been any attempt to actually get this into standard Java? Or some project taking this a few steps further?Germano
The article above does reference a proprietary Java compiler which implements tuples. I don't think it's been considered in Standard Java though.Stutzman
I found a few references to this extension (named Spar/Java, it seems) in several research papers between 2001 and 2008. Would be interesting if anyone is still working on it (or why it was dropped if not).Germano
There is some activity in the Java community around this. See for instance: blogs.oracle.com/jrose/entry/tuples_in_the_vmStutzman
L
6

Create a class that describes the concept you're actually modeling and use that. It can just store two Set<Long> and provide accessors for them, but it should be named to indicate what exactly each of those sets is and why they're grouped together.

Langille answered 19/4, 2010 at 21:29 Comment(2)
Ah, the question is just wrong. Are these ordered? Can have duplicate elements? What about a tuple? ;)Conjunctivitis
Worth mentioning Auto/Value here - it makes creating your own types safe and painless. It's arguably easier than using a generic Pair or Tuple type.Cupcake
Z
5

To supplement @maerics's answer, here is the Comparable tuple:

import java.util.*;

/**
 * A tuple of two classes that implement Comparable
 */
public class ComparableTuple<X extends Comparable<? super X>, Y extends Comparable<? super Y>>
       extends Tuple<X, Y>
       implements Comparable<ComparableTuple<X, Y>>
{
  public ComparableTuple(X x, Y y) {
    super(x, y);
  }

  /**
   * Implements lexicographic order
   */
  public int compareTo(ComparableTuple<X, Y> other) {
    int d = this.x.compareTo(other.x);
    if (d == 0)
      return this.y.compareTo(other.y);
    return d;
  }
}
Zarathustra answered 5/1, 2013 at 18:18 Comment(3)
This too suffers from the question's wrong assumption that tuples should be Pairs.... tuples are of any length, not just 2.Conjunctivitis
@naxa Better something than nothing. Also, you can nest pairs.Zarathustra
@AlexeiAverchenko, Yea a cluster of 4 pairs for a Tuple-5, gross........Harkey
A
3

You can use Google Guava Table

Arteriosclerosis answered 7/4, 2014 at 11:2 Comment(0)
M
1

I will start from a general point of view about tuples in Java and finish with an implication for your concrete problem.

1) The way tuples are used in non-generic languages is avoided in Java because they are not type-safe (e.g. in Python: tuple = (4, 7.9, 'python')). If you still want to use something like a general purpose tuple (which is not recommended), you should use Object[] or List<Object> and cast the elements after a check with instanceof to assure type-safety.

Usually, tuples in a certain setting are always used the same way with containing the same structure. In Java, you have to define this structure explicitly in a class to provide well-defined, type-safe values and methods. This seems annoying and unnecessairy at first but prevents errors already at compile-time.

2) If you need a tuple containing the same (super-)classes Foo, use Foo[], List<Foo>, or List<? extends Foo> (or the lists's immutable counterparts). Since a tuple is not of a defined length, this solution is equivalent.

3) In your case, you seem to need a Pair (i.e. a tuple of well-defined length 2). This renders maerics's answer or one of the supplementary answers the most efficient since you can reuse the code in the future.

Modillion answered 19/11, 2014 at 9:48 Comment(2)
Tuples are not safe? That's bs. Look at C++ tuples for example (not to mention Haskell, Scala and others). If container can store values of different types, doesn't mean it is not type-safe. If anything, it's Java's limitations that prevent implementation of proper tuples.Desk
@DanM. you are right. I edited my response accordingly to explicitly refer to non-generic languages only. Please consider to phrase your comments in a polite way.Modillion

© 2022 - 2024 — McMap. All rights reserved.