tags:

views:

3407

answers:

6

I like how Java has a Map where you can define the types of each entry in the map, for example <String, Integer>.

What I'm looking for is a type of collection where each element in the collection is a pair of values. Each value in the pair can have its own type (like the String and Integer example above), which is defined at declaration time.

The collection will maintain its given order and will not treat one of the values as a unique key (as in a map).

Essentially I want to be able to define an ARRAY of type <String,Integer> or any other 2 types.

I realize that I can make a class with nothing but the 2 variables in it, but that seems overly verbose.

I also realize that I could use a 2D array, but because of the different types I need to use, I'd have to make them arrays of OBJECT, and then I'd have to cast all the time.

I only need to store pairs in the collection, so I only need two values per entry. Does something like this exist without going the class route? Thanks!

+8  A: 

You could write a generic Pair<A, B> class and use this in an array or list. Yes, you have to write a class, but you can reuse the same class for all types, so you only have to do it once.

Dan Dyer
I'd love to see an example of that!
ZenBlender
Dan, the bad thing about that is that it is not possible to accept e.g. only Pair<String,Integer>s because of type erasure, no?But so is Java...
Johannes Weiß
Interesting, Johannes. http://java.sun.com/docs/books/tutorial/java/generics/erasure.html
JMD
I don't think this will work for plain arrays, but it definitely will work for other Collections.
Outlaw Programmer
@Outlaw Programmer: Good point, you can use it with arrays, but it's ugly as you have to use a hack to create generic arrays.
Dan Dyer
@Johannes: Erasure is no more of a problem here than it is anywhere else. If you declare a field as Pair<String, Integer>, all usages will be checked at compile time. As with other collections, you can do unsafe things, but they will trigger compiler warnings.
Dan Dyer
the thing about type erasure: sorry, nonsense...
Johannes Weiß
+2  A: 

I was going to ask if you would not want to just use a List<Pair<T, U>>? but then, of course, the JDK doesn't have a Pair<> class. But a quick Google found one on both Wikipedia, and forums.sun.com. Cheers

JMD
+10  A: 

The Pair class is one of those "gimme" generics examples that easy enough to write on your own. For example, off the top of my head:

public class Pair<L,R> {

  private final L left;
  private final R right;

  public Pair<L,R>(L left, R right) {
    this.left = left;
    this.right = right;
  }

  public L getLeft() { return left; }
  public R getRight() { return right; }

  public int hashCode() { return left.hashCode() ^ right.hashCode(); }

  public boolean equals(Object o) {
    if (o == null) return false;
    if (!(o instanceof Pair)) return false;
    Pair pairo = (Pair) o;
    return this.left.equals(pairo.getLeft()) &&
           this.right.equals(pairo.getRight());
  }

}

And yes, this exists in multiple places on the Net, with varying degrees of completeness and feature. (My example above is intended to be immutable.)

Paul Brinkley
I like this, but what do you think about making the left and right fields public? It's pretty clear that the Pair class is never going to have any logic associated and all clients will need access to 'left' and 'right,' so why not make it easy?
Outlaw Programmer
That would kill the immutability of it. This implementation is inherently thread safe at the moment.
sdellysse
Um...no it wouldn't. The fields are marked as final, so they can't be reassigned. And it's not threadsafe because 'left' and 'right' could be mutable. Unless getLeft()/getRight() returned defensive copies (useless in this case), I don't see what the big deal is.
Outlaw Programmer
I agree with Outlaw. Plus, it simplifies the idiom for Pair access; Pair is essentially a throwaway placeholder class anyway. The only reason I might NOT do it is if I planned to extend Pair and override left/right access behavior somehow.
Paul Brinkley
Meanwhile, to be fair, the Pair code above is threadsafe with respect to itself; that is, it introduces no additional danger that wasn't already innate to the safety of the contained objects. :-)
Paul Brinkley
...oh fargle, scratch that. It -does- introduce safety problems in the hashCode and equals methods.
Paul Brinkley
A: 

You could use

Map<String,Map<Integer,Double>>...
Milhous
+1  A: 

The built in AbstractMap.SimpleEntry or AbstractMap.SimpleImmutableEntry classes are an option, too.

EDIT: removed nonsense about type erasure.

Johannes Weiß
Your can't write a function that only accepts generics with specific type parameters? Really... because, I just did. Type erasure has it's limitations, but that isn't one of them (sure you could coerce your compiler into ignoring the fact that you're passing in the wrong types...
Aaron Maenpaa
... but then you deserve the ClassCastException that you will eventually get.)
Aaron Maenpaa
oh, your right, thanks for the point!
Johannes Weiß
+1  A: 

The preferred solution as you've described it is a List of Pairs (i.e. List).

To accomplish this you would create a Pair class for use in your collection. This is a useful utility class to add to your code base.

The closest class in the Sun JDK providing functionality similar to a typical Pair class is AbstractMap.SimpleEntry. You could use this class rather than creating your own Pair class, though you would have to live with some awkward restrictions and I think most people would frown on this as not really the intended role of SimpleEntry. For example SimpleEntry has no "setKey()" method and no default constructor, so you may find it too limiting.

Bear in mind that Collections are designed to contain elements of a single type. Related utility interfaces such as Map are not actually Collections (i.e. Map does not implement the Collection interface). A Pair would not implement the Collection interface either but is obviously a useful class in building larger data structures.

Jeremy Rishel