In my opinion, there is no Pair in Java because, if you want to add extra functionality directly on the pair (e.g. Comparable), you must bound the types. In C++, we just don't care, and if types composing a pair do not have operator <, the pair::operator < will not compile as well.
An example of Comparable with no bounding:
public class Pair<F, S> implements Comparable<Pair<? extends F, ? extends S>> { public final F first; public final S second; /* ... */ public int compareTo(Pair<? extends F, ? extends S> that) { int cf = compare(first, that.first); return cf == 0 ? compare(second, that.second) : cf; } //Why null is decided to be less than everything? private static int compare(Object l, Object r) { if (l == null) { return r == null ? 0 : -1; } else { return r == null ? 1 : ((Comparable) (l)).compareTo(r); } } } /* ... */ Pair<Thread, HashMap<String, Integer>> a = /* ... */; Pair<Thread, HashMap<String, Integer>> b = /* ... */; //Runtime error here instead of compile error! System.out.println(a.compareTo(b));
An example of Comparable with compile-time check for whether type arguments are comparable:
public class Pair< F extends Comparable<? super F>, S extends Comparable<? super S> > implements Comparable<Pair<? extends F, ? extends S>> { public final F first; public final S second; /* ... */ public int compareTo(Pair<? extends F, ? extends S> that) { int cf = compare(first, that.first); return cf == 0 ? compare(second, that.second) : cf; } //Why null is decided to be less than everything? private static < T extends Comparable<? super T> > int compare(T l, T r) { if (l == null) { return r == null ? 0 : -1; } else { return r == null ? 1 : l.compareTo(r); } } } /* ... */ //Will not compile because Thread is not Comparable<? super Thread> Pair<Thread, HashMap<String, Integer>> a = /* ... */; Pair<Thread, HashMap<String, Integer>> b = /* ... */; System.out.println(a.compareTo(b));
This is good, but this time you may not use non-comparable types as type arguments in Pair. One may use lots of Comparators for Pair in some utility class, but C++ people may not get it. Another way is to write lots of classes in a type hierarchy with different bounds on type arguments, but there are too many possible bounds and their combinations...
AbstractMap.SimpleEntryconvoluted?