Rhino, read that sentence again.
And that one.
And again.
Now proceed with the rest of this post.
For a second there, I had no idea what you were proposing.
But I reviewed the Collection Classes in the Java Tutorial and I think I
get it now.
In all honesty, I hadn't even thought of SortedMap when I wrote the code
again the other day. I just knew that I wanted the result to be in
alphabetical order, not random the way that Locales.getAvailableLocales()
provides them.
You make that desire concrete by the type of the object holding the
locales - you want them to be sorted, so it's a SortedMap.
The article on the Map interface pointed out that TreeMap would assure
that I had alphabetical order so I went with that. Now that you've
reminded me about SortedMap, I can see the merit of it. It's not much
different than TreeMap but it does give those additional features, like
range operations. I don't see those as being _necessary_ for my humble
little getLocales() method, which I'm really just writing for myself,
but some future user of the class could conceivably benefit from those
extra features. Or maybe _I_ will get a benefit from those features a
little further down the road!
You're on the wrong track here. The extra features are not what SortedMap
is about - it's fundamentally about that first paragraph in its javadoc:
A Map that further provides a total ordering on its keys. The map is
ordered according to the natural ordering of its keys [...] This order is
reflected when iterating over the sorted map's collection views (returned
by the entrySet, keySet and values methods). Several additional
operations are provided to take advantage of the ordering.
Yes, there are several additional operations. But the heart of the matter
is that the map has an order, which governs iteration over its contents.
I've modified the code to produced a SortedMap - just replaced all "Map"
with "SortedMap", dead easy! - and reran my unit tests. Naturally, they
still worked fine.
Hmm. Did I do this right?
I had this:
=========================================================================
public Map<String, Locale> getLocales() {
Map<String, Locale> sortedLocales = new TreeMap<String, Locale>();
for (Locale oneLocale : Locale.getAvailableLocales()) {
sortedLocales.put(oneLocale.getDisplayName(locale), oneLocale);
}
return sortedLocales;
}
========================================================================
and changed it to:
========================================================================
public SortedMap<String, Locale> getLocales() {
SortedMap<String, Locale> sortedLocales = new TreeMap<String, Locale>();
for (Locale oneLocale : Locale.getAvailableLocales()) {
sortedLocales.put(oneLocale.getDisplayName(locale), oneLocale);
}
return sortedLocales;
}
========================================================================
Spot on.
The first line of the revised method looks a bit funny:
SortedMap ... = TreeMap ....
Did I do what you meant?
Yes. You did *exactly* what Lew meant when he said:
It's mostly a good idea to declare variables as an interface type rather
than a concrete type.
SortedMap is an interface type - it says 'this map is sorted somehow'.
TreeMap is a concrete type - it says 'this map is implemented as a
red-black tree, which incidentally results in it being sorted'.
Sigh! I still struggle with understanding what I am doing sometimes.... I
still don't REALLY understand the difference between these:
- Map<String, Locale> sortedLocales = new TreeMap<String, Locale>();
It happens to be a TreeMap, but all the type declares is that it's a map.
- SortedMap<String, Locale> sortedLocales = new TreeMap<String, Locale>();
It happens to be a TreeMap, but all the type declares is that it's a
sorted map.
- Map<String, Locale> sortedLocales = new SortedMap<String, Locale>();
Illegal. SortedMap is an interface.
Or, by the same token:
- Calendar cal = Calendar.getInstance();
- Calendar cal = new GregorianCalendar();
- GregorianCalendar gcal = new GregorianCalendar();
Those last two examples are an exact analogue. The first one is a little
different - but a natural extension of the idea.
The third version says "I care that this is object is specifically a
GregorianCalendar. I am going to do things with it which specifically
would not work with any other kind of calendar.". There are times when you
might want to say that, but you would strive to be more general - in which
case you would use the second version, which says "I care that this object
is a Calendar, but i don't care which kind of calendar - it could be
Gregorian, Islamic, Japanese Imperial, Darian Jovian, etc". However, in
the second form, although you will proceed to write your code
calendar-agnostically, you are actually hardcoding the choice of
GregorianCalendar there. You could change it later, but you'd have to go
through your code and change the constructions. In the first form, you
push the calendar-agnosticism even into the creation itself - rather than
explicitly using a GregorianCalendar, you let Calendar decide what kind of
calendar should be used. Right now, that will presumably always be a
GregorianCalendar, but if at some point someone boots up your software on
Ganymede (the moon, not the Eclipse release), it should probably be
Darian, and by leaving the decision to Calendar.getInstance, you let that
happen without having to alter your code. You delegate the decision to
someone in a better position to make it.
Now, in this particular case, there is a major caveat: different types of
Calendar are not interchangeable in the way that different types of Map
are, because they represent different ideas. If you're using this Calendar
object to handle dates which are definitively in Gregorian (January,
February and all that), then you need an actual GregorianCalendar, and you
should declare your variables accordingly. To paraphrase Einstein,
"everything should be made as generic as possible, but no more so".
If I had to write an exam, clearly articulating the distinction between
those and what the implications of each is, I'd surely make a hash of
it....
That would be a real mistake. You should be making a tree. Have you
learned NOTHING?
I can tell if I'm getting a compile error and I can tell if my code is
doing what I want it to do but I still don't always understand WHY one
thing is better than an other or what the real difference is between
things that look very very similar.....
The choice between Map and SortedMap here is not one of correctness. It's
one of design and style. Good design and style doesn't change the way code
runs, but it makes it easier to understand and modify. A huge part of the
business of software is understanding and modifying existing code, and so
good design and style are valuable. They're investment for the future,
rather than the here and now.
tom