tags:

views:

24440

answers:

10

Is there a good reason why there is no Pair in Java? What would be the equivalent of this C++ construct? I would rather avoid reimplementing my own.

It seems that 1.6 is providing something similar (AbstractMap.SimpleEntry), but this looks quite convoluted.

+8  A: 

It depends on what you want to use it for. The typical reason to do so is to iterate over maps, for which you simply do this (Java 5+):

Map<String, Object> map = ... ; // just an example
for (Map.Entry<String, Object> entry : map.entrySet()) {
  System.out.printf("%s -> %s\n", entry.getKey(), entry.getValue());
}
cletus
+3  A: 

Here's a Java Pair<> class I just now stole from Wikipedia here (so I can't vouch for how good it is):

public class Pair<T, S>
{
  public Pair(T f, S s)
  { 
    first = f;
    second = s;   
  }

  public T getFirst()
  {
    return first;
  }

  public S getSecond() 
  {
    return second;
  }

  public String toString()
  { 
    return "(" + first.toString() + ", " + second.toString() + ")"; 
  }

  private T first;
  private S second;
}
Michael Burr
Chris's critique time! 1. There's really no reason to keep the fields private, or to have getters. Just make them public, like std::pair. 2. the toString() method calls are all redundant in this case. 3. Why T and S? Why not T1 and T2?
Chris Jester-Young
I can't be bothered to edit Wikipedia at this time, so I've decided to just critique it here. :-P
Chris Jester-Young
Have at it! Just try not to blame me...
Michael Burr
CJYMutability is evil, it makes simple things complex, just like pass by ref makes life harder than by value.
mP
+1  A: 

The problem is that "pair" usually does not indicate very well what exactly the relationship between the two objects is.

For instance, in a map in c++ you insert a pair to add a value, but you really are just saying you want to put a value in the map, which you later want to retrieve using a particular key. This key may or may not already exist in that map.

Is there any particular reason you need a pair type?

Hans
"Is there any particular reason you need a pair type?" One good reason would be to have a typesafe way to return two values from a method.
Thilo
+26  A: 

In this thread on comp.lang.java.help, Hunter Gratzner gives some arguments against the presence of a Pair construct in Java. The main argument is that a class Pair doesn't convey any semantics about the relationship between the two values (how do you know what "first" and "second" mean ?).

A better practice is to write a very simple class, like the one Mike proposed, for each application you would have made of the Pair class. Map.Entry is an example of a pair that carry its meaning in its name.

To sum up, in my opinion it is better to have a class Position(x,y), a class Range(begin,end) and a class Entry(key,value) rather than a generic Pair(first,second) that doesn't tell me anything about what it's supposed to do.

Luc Touraille
Does java not have typedefs?
tfinniga
@tfinniga: it does not.
sixlettervariables
I think the AbstractPair<F,S> idea from that thread is excellent
Craig
Question: So you'd not prefer var range = new pair<int, int>(); at all? or ever use it.
CasperT
It all depends on your application. If I scarcely use ranges, then I could choose having pair objects named 'range'. However, it might be better to have 'range' denote a (possibly generic) type and create objects of that type, e.g. Range<double> confidenceInterval, Range<Iterator> inputSequence and so on. As always, there is not a one-fit-all solution, but the more information I get when looking at my code, the better it is.
Luc Touraille
To clarify, "Hunter Gratzner" smooshes together the names of Ian Hunter and Matthew Gratzner. http://huntergratzner.com/#/Bios
schultkl
It's silly to say a custom-made class is *always* a "better practice". Sometimes, a "pair of values" is all you need. Of course, if you need a position, make a position class. And when you need a range, make a range class. But why, when you need a pair, should the standard library not provide a pair class?
jalf
Java doesn't pass arguments by references because passing arguments by reference is bad (though C# does it nicely). Java doesn't have a Pair<> type because Pair<> type is bad (I agree with this one). So each each time I want to return several (related) items from a single function -- make a new class ? OK but then I'll go for a (static) inner class (no additional file) and keep my fields public (no getter/setter cruft). Political correctness has to give way at some point.
@jalf, that's typical Java way of thinking. That's just how they roll.
Alex B
@Alex, you may be writing in java but you don't have to write in the java way... Personally, I like Java as a high-performance language but don't care for the Java way, so my java code looks a bit different...
Vuntic
@Vuntic Well yeah. Though, you still do have to use libraries and frameworks from Java ecosystem if you want anything done in reasonable time.
Alex B
+14  A: 

HashMap compatible Pair class:

public class Pair<A, B> {
    private A first;
    private B second;

    public Pair(A first, B second) {
     super();
     this.first = first;
     this.second = second;
    }

    public int hashCode() {
     int hashFirst = first != null ? first.hashCode() : 0;
     int hashSecond = second != null ? second.hashCode() : 0;

     return (hashFirst + hashSecond) * hashSecond + hashFirst;
    }

    public boolean equals(Object other) {
     if (other instanceof Pair) {
      Pair otherPair = (Pair) other;
      return 
      ((  this.first == otherPair.first ||
       ( this.first != null && otherPair.first != null &&
         this.first.equals(otherPair.first))) &&
       ( this.second == otherPair.second ||
       ( this.second != null && otherPair.second != null &&
         this.second.equals(otherPair.second))) );
     }

     return false;
    }

    public String toString()
    { 
           return "(" + first + ", " + second + ")"; 
    }

    public A getFirst() {
     return first;
    }

    public void setFirst(A first) {
     this.first = first;
    }

    public B getSecond() {
     return second;
    }

    public void setSecond(B second) {
     this.second = second;
    }
}
arturh
You probably want to delete the setters, and make first and second final, thus making the pair immutable. (If someone changed the components after using them as a hash key, weird things will happen).
Thilo
return "(" + first.toString() + ", " + second.toString() + ")" in toString() method may throw NullPointerExceptions. This is better: return "(" + first + ", " + second + ")";
Juha Syrjälä
sargas
Sorry for the random nooby question, but why do you have a call to super() in the constructor?
Ibrahim
@Ibrahim: In this case, it's superfluous---the behaviour is exactly the same if you took the `super()` out. Normally I'd just lop it off if it's optional, like it is here.
Chris Jester-Young
+1  A: 

Pair would be a good stuff, to be a basic construction unit for a complex generics, for instance, this is from my code:

WeakHashMap<Pair<String, String>, String> map = ...

It is just the same as Haskell's Tuple

Shaman
Now I can say, that using Pair<A,B> makes code less informative, and implementing special objects instead of using Pair is much better
Shaman
+8  A: 

This is Java. You have to make your own tailored Pair class with descriptive class and field names, and not to mind that you will reinvent the wheel by writing hashCode()/equals() or implementing Comparable again and again.

Andreas Krey
+1  A: 

Another way to implement Pair with.

  • Public immutable fields, i.e. simple data structure.
  • Comparable.
  • Simple hash and equals.
  • Simple factory so you don't have to provide the types. e.g. Pair.of("hello", 1);

    public class Pair<FIRST, SECOND> implements Comparable<Pair<FIRST, SECOND>> {
    public final FIRST first;
    public final SECOND second;
    
    
    private Pair(FIRST first, SECOND second) {
        this.first = first;
        this.second = second;
    }
    
    
    public static <FIRST, SECOND> Pair<FIRST, SECOND> of(FIRST first, SECOND second) {
        return new Pair<FIRST, SECOND>(first, second);
    }
    
    
    @Override
    public int compareTo(Pair<FIRST, SECOND> o) {
        int cmp = compare(first, o.first);
        return cmp == 0 ? compare(second, o.second) : cmp;
    }
    // todo move this to a helper class.
    private static int compare(Object o1, Object o2) {
        return o1 == null ? o2 == null ? 0 : -1 : o2 == null ? +1 : ((Comparable) o1).compareTo(o2);
    }
    
    
    @Override
    public int hashCode() {
        return 31 * hashcode(first) + hashcode(second);
    }
    
    
    // todo move this to a helper class.
    private static int hashcode(Object o) {
        return o == null ? 0 : o.hashCode();
    }
    
    
    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Pair)) return false;
        if (this == obj) return true;
        return equal(first, ((Pair) obj).first) && equal(second, ((Pair) obj).second);
    }
    
    
    // todo move this to a helper class.
    private boolean equal(Object o1, Object o2) {
        return o1 == null ? o2 == null : (o1 == o2 || o1.equals(o2));
    }
    
    
    @Override
    public String toString() {
        return "(" + first + ", " + second + ')';
    }
    }
    
Peter Lawrey
A: 

Simple way Object [] - can be use as anу dimention tuple

Testus
A: 

"The main argument is that a class Pair doesn't convey any semantics about the relationship between the two values"

You could say the same about a basic type such as an 'int'. "Int" doesn't convey any semantics about what kind of integer it is - is it money, or temperature, number of people in the bus? But it would be silly to make everyone write a special int class (e.g. NumberOfPeople) every time they need to use a number in their program. For really simple things, such as ints, doubles and pairs there is no need to be so extreme.

John Smith