Is it good practice to use ordinal of enum? - java

I have an enum:
public enum Persons {
CHILD,
PARENT,
GRANDPARENT;
}
Is there any problem with using ordinal() method to check "hierarchy" between enum members? I mean - is there any disadvantages when using it excluding verbosity, when somebody can change accidentally order in future.
Or is it better to do something like that:
public enum Persons {
CHILD(0),
PARENT(1),
GRANDPARENT(2);
private Integer hierarchy;
private Persons(final Integer hierarchy) {
this.hierarchy = hierarchy;
}
public Integer getHierarchy() {
return hierarchy;
}
}

TLDR: No, you should not!
If you refer to the javadoc for ordinal method in Enum.java:
Most programmers will have no use for this method. It is
designed for use by sophisticated enum-based data structures, such
as java.util.EnumSet and java.util.EnumMap.
Firstly - read the manual (javadoc in this case).
Secondly - don't write brittle code. The enum values may change in future and your second code example is much more clear and maintainable.
You definitely don't want to create problems for the future if a new enum value is (say) inserted between PARENT and GRANDPARENT.

As suggested by Joshua Bloch in Effective Java, it's not a good idea to derive a value associated with an enum from its ordinal, because changes to the ordering of the enum values might break the logic you encoded.
The second approach you mention follows exactly what the author proposes, which is storing the value in a separate field.
I would say that the alternative you suggested is definitely better because it is more extendable and maintainable, as you are decoupling the ordering of the enum values and the notion of hierarchy.

The first way is not straight understandable as you have to read the code where the enums are used to understand that the order of the enum matters.
It is very error prone.
public enum Persons {
CHILD,
PARENT,
GRANDPARENT;
}
The second way is better as it is self explanatory :
CHILD(0),
PARENT(1),
GRANDPARENT(2);
private SourceType(final Integer hierarchy) {
this.hierarchy = hierarchy;
}
Of course, orders of the enum values should be consistent with the hierarchical order provided by the enum constructor arguments.
It introduces a kind of redundancy as both the enum values and the arguments of the enum constructor conveys the hierarchy of them.
But why would it be a problem ?
Enums are designed to represent constant and not frequently changing values.
The OP enum usage illustrates well a good enum usage :
CHILD, PARENT, GRANDPARENT
Enums are not designed to represent values that moves frequently.
In this case, using enums is probably not the best choice as it may breaks frequently the client code that uses it and besides it forces to recompile, repackage and redeploy the application at each time an enum value is modified.

First, you probably don't even need a numeric order value -- that's
what Comparable
is for, and Enum<E> implements Comparable<E>.
If you do need a numeric order value for some reason, yes, you should
use ordinal(). That's what it's for.
Standard practice for Java Enums is to sort by declaration order,
which is why Enum<E> implements Comparable<E> and why
Enum.compareTo() is final.
If you add your own non-standard comparison code that doesn't use
Comparable and doesn't depend on the declaration order, you're just
going to confuse anyone else who tries to use your code, including
your own future self. No one is going to expect that code to exist;
they're going to expect Enum to be Enum.
If the custom order doesn't match the declaration order, anyone
looking at the declaration is going to be confused. If it does
(happen to, at this moment) match the declaration order, anyone
looking at it is going to come to expect that, and they're going to
get a nasty shock when at some future date it doesn't. (If you write
code (or tests) to ensure that the custom order matches the
declaration order, you're just reinforcing how unnecessary it is.)
If you add your own order value, you're creating maintenance headaches
for yourself:
you need to make sure your hierarchy values are unique
if you add a value in the middle, you need to renumber all
subsequent values
If you're worried someone could change the order accidentally in the
future, write a unit test that checks the order.
In sum, in the immortal words of Item 47:
know and use the libraries.
P.S. Also, don't use Integer when you mean int. 🙂

If you only want to create relationships between enum values, you can actually use the trick of using other enum values:
public enum Person {
GRANDPARENT(null),
PARENT(GRANDPARENT),
CHILD(PARENT);
private final Person parent;
private Person(Person parent) {
this.parent = parent;
}
public final Parent getParent() {
return parent;
}
}
Note that you can only use enum values that were declared lexically before the one you're trying to declare, so this only works if your relationships form an acyclic directed graph (and the order you declare them is a valid topological sort).

Using ordinal() is unrecommended as changes in the enum's declaration may impact the ordinal values.
UPDATE:
It is worth noting that the enum fields are constants and can have duplicated values, i.e.
enum Family {
OFFSPRING(0),
PARENT(1),
GRANDPARENT(2),
SIBLING(3),
COUSING(4),
UNCLE(4),
AUNT(4);
private final int hierarchy;
private Family(int hierarchy) {
this.hierarchy = hierarchy;
}
public int getHierarchy() {
return hierarchy;
}
}
Depending on what you're planning to do with hierarchy this could either be damaging or beneficial.
Furthermore, you could use the enum constants to build your very own EnumFlags instead of using EnumSet, for example

I would use your second option (using a explicit integer) so the numeric values are assigned by you and not by Java.

Let's consider following example:
We need to order several filters in our Spring Application. This is doable by registering filters via FilterRegistrationBeans:
#Bean
public FilterRegistrationBean compressingFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(compressingFilter());
registration.setName("CompressingFilter");
...
registration.setOrder(1);
return registration;
}
Let's assume we have several filters and we need to specify their order (e.g. we want to set as first the filter which add do MDC context the JSID for all loggers)
And here I see the perfect usecase for ordinal(). Let's create the enum:
enum FilterRegistrationOrder {
MDC_FILTER,
COMPRESSING_FILTER,
CACHE_CONTROL_FILTER,
SPRING_SECURITY_FILTER,
...
}
Now in registration bean we can use:
registration.setOrder(MDC_FILTER.ordinal());
And it works perfectly in our case. If we haven't had an enum to do that we would have had to re-numerate all filters orders by adding 1 to them (or to constants which stores them). When we have enum you only need to add one line in enum in proper place and use ordinal. We don't have to change the code in many places and we have the clear structure of order for all our filters in one place.
In the case like this I think the ordinal() method is the best option to achieve the order of filters in clean and maintainable way

You must use your judgement to evaluate which kind of errors would be more severe in your particular case. There is no one-size-fits-all answer to this question. Each solution leverages one advantage of the compiler but sacrifices the other.
If your worst nightmare is enums sneakily changing value: use ENUM(int)
If your worst nightmare is enum values becoming duplicated or losing contiguousness: use ordinal.

According to java doc
Returns the ordinal of this enumeration constant (its position in its
enum declaration, where the initial constant is assigned an ordinal of
zero). Most programmers will have no use for this method. It is
designed for use by sophisticated enum-based data structures, such as
EnumSet and EnumMap.
You can control the ordinal by changing the order of the enum, but you cannot set it explicitly.One workaround is to provide an extra method in your enum for the number you want.
enum Mobile {
Samsung(400), Nokia(250),Motorola(325);
private final int val;
private Mobile (int v) { val = v; }
public int getVal() { return val; }
}
In this situation Samsung.ordinal() = 0, but Samsung.getVal() = 400.

This is not a direct answer to your question. Rather better approach for your usecase. This way makes sure that next developer will explicitly know that values assigned to properties should not be changed.
Create a class with static properites which will simulate your enum:
public class Persons {
final public static int CHILD = 0;
final public static int PARENT = 1;
final public static int GRANDPARENT = 2;
}
Then use just like enum:
Persons.CHILD
It will work for most simple use cases. Otherwise you might be missing on options like valueOf(), EnumSet, EnumMap or values().

Related

What is a good extensible way to check if a string is a value of more than one Enum Type

Lets say I have a bunch of logically distinct java enums that cannot be merged together since certain prior operations depend on each independent enum.
enum Enum1 {
ENUM1VAL1,
ENUM1VAL2
}
enum Enum2 {
ENUM2VAL1,
ENUM2VAL2
}
void existingOperationOnEnum1(Enum1 enum1);
void existingOperationOnEnum2(Enum2 enum2);
Now lets say I wish to define a new operation that checks whether a string can be converted to any of these enums
void isValidStringConversionForEnums(final String input) {
ENUM1.valueOf(input); // Throws IllegalArgumentException if conversion not possible
ENUM2.valueOf(input); // Throws IllegalArgumentException if conversion not possible
}
So this is great, but its not extensible. The business logic is such that we can have new enums be created in the future (with its own set of enum specific operations), however isValidStringConversionForEnums needs to be updated every time a new one is added, which a programmer might easily forget.
So my question is how would I modify isValidStringConversionForEnums so that it works with future extensions.
One way I thought was to create an interface EnumBase, get the Enums to extend that and then use reflection to get all Enum impls, but I do not wish to use Reflection in Runtime unless I have to.
Is there an alternate way in which this can be written such that I add a new enum and somehow isValidStringConversionForEnums handles that new enum.
You have 2 independent problems here:
Given a list of enum types, how do you write code that tests that your string is convertible to at least one of them?
How do you obtain that list.
These 2 problems are completely unrelated. So, you solve them separately.
Given a list...
Class<? extends Enum> is probably your best bet.
enum Test1 {
FOO, BAR;
}
enum Test2 {
FOO, BAZ;
}
public static void isValidStringConversionForEnums(String in, Class<? extends Enum>... enumTypes) {
for (var enumType : enumTypes) {
for (var c : enumType.getEnumConstants()) {
if (c.name().equals(in)) return true;
}
}
return false;
}
This isn't particularly efficient - but you get the gist. You can pre-make a HashSet containing every valid value and then it's just a .containsKey() call, using the above code to instead serve as a one-off "init" that makes that HashSet for example. If you actually need the enum constants, make a Map<String, List<? extends Enum<?>> instead. computeIfAbsent is a nice way to create those lists on the fly in a one-liner.
Discovery
The java classloader abstraction system simply doesn't have 'give me a list of all files in the jar' or 'give me a list of all classes'. Some libraries appear to provide those features but they are hacks that do not always work, so you probably don't want to use those.
The standard solution for this problem, used by the JVM itself too, is 'SPI' - with module-info.java it's baked in, if you don't use that (and you probably shouldn't, it's kinda tricky, that), there's ServiceLoader. To make the service files, you can use annotation processors, I think there are a few around that automate it.
Then the process flow is simply, to make a new enum, stick a #ProviderFor(Something.class) on it, where Something.class is some marker interface of your design.
Note that wanting to scan all enums on the entire classpath is just plain bad. There are all sorts of enums that java uses internally, what if one of those so happens to have a value in there equal to one of your strings? That does NOT all of a sudden make that a valid string. No, only enums you explicitly mark as applying to whatever this question is, qualify, this, you need to do something to mark them. Either with an annotation, or explicitly by writing out the full list.

Understanding enumset for enumerators

As far as I understood it would be much easier and clearler to use EnumSet and that the Set was specifically designed for using with Enumerators. My question is whether we should consider to use the EnumSet every time we need to maintain some collection of enumerators. For instance, I have he following enum:
public enum ReportColumns{
ID,
NAME,
CURRENCY
//It may contain much more enumerators than I mentioned here
public int getMaintenanceValue(){
//impl
}
}
And I need to use some collection ofthe enum in the method:
public void persist(Collection<ReportColumns> cols){
List<Integer> ints = new LinkedList<>();
for(ReportColumn c: cols){
ints.add(c.getMaintenanceValue());
}
//do some persistance-related operations
}
so, if I don't care about if the collection is ordered or not should I use EnumSet<E> every time to improve performance?
As a rule of thumb, whenever you create a collection of enums and don't care about their order, you should create an EnumSet. As you mentioned, it would give you a slight increase in performance (in fact, most static code analysis tools I know actually warn about not using it).
For a method declaration though, I wouldn't. As another rule of thumb, methods should use the "highest" type possible that still makes sense. The public contract of this method should be "give me a bunch of enums, and I'll persist them". The method shouldn't care what collection is passed, so it makes no sense forcing the parameter type to be an EnumSet. If the concrete type you pass is indeed an EnumSet you'll get all the performance benefits anyway.

Finding Java Enum types from code values?

We use code values in our database, and Enums in Java. When querying the database, we need to take a code value and get an Enum instance.
Is it overkill to have a HashMap to avoid iteration? What would you do? Is there an easier way?
public enum SomeEnum
{
TYPE_A(2000), TYPE_B(2001);
private int codeValue;
private static HashMap<Integer, SomeEnum> codeValueMap = new HashMap<Integer, SomeEnum>(2);
static
{
for (SomeEnum type : SomeEnum.values())
{
codeValueMap.put(type.codeValue, type);
}
}
//constructor and getCodeValue left out
public static SomeEnum getInstanceFromCodeValue(int codeValue)
{
return codeValueMap.get(codeValue);
}
}
That's exactly the approach I'd take to solve that particular problem. I see nothing wrong with it from a design point of view, it's intuitive, efficient and (as far as I can see) does exactly what it should.
The only other sensible approach I can think of would be to have the map in a separate class and then call that class to update the map from SomeEnum's constructor. Depending on the use case, this separation could be beneficial - but unless it would have a hard benefit I would take your approach and encapsulate everything within the enum itself.
Thanks, I guess my main concern is memory usage, and if it is worth it.
Unless that enum has thousands of values, memory usage will be trivial. (And if it does have thousands of values, then using iteration to do the lookup would be a major performance killer.)
This is a sensible use of memory, IMO.
Perhaps I am over thinking this.
Perhaps you are.
I think in this case we can't avoid iteration. It's either HashMap doing it, or we wrote our own iteration code.
If performance really does matter maybe you can try a binary tree approach.
If your enum space is dense, that is, not a lot of unused values, you could use the toString() and valueOf() methods. Name your values with a common string prefix, then attach the prefix before using valueOf() and strip it after using toString(). This has the disadvantage that you would have to convert to a numeric value if that's how it's stored in your database.
Alternatively, you could add common methods for conversion and assign your database value to a specific enum value.
Both these techniques have the advantage of leveraging the design of enum classes.
There is a lot of good, mind-bending information about enums (and Java, in general) at http://mindprod.com/jgloss/enum.html.
Though, there's nothing wrong with your way if it does the job you want.
That's fine. Don't worry about tiny performance differences.
One would think that if there are only two instances for an enum, like in your example, a trivial code of iterating would be faster:
public static SomeEnum getInstanceFromCodeValue(int codeValue)
{
for (SomeEnum type : SomeEnum.values()) {
if(type.codeValue == codeValue)
return type;
}
}
But there's a hidden cost, quite expensive one if we do care about performane at such level. It's fixable, but you need to see it first:)
To get the ID:
EnumDay day = EnumDay.WEDNESDAY;
int myID = day.ordinal();
To load the day from the myID:
EnumDay dayCopy = EnumDay.values()[myID];

Help matching fields between two classes

I'm not too experienced with Java yet, and I'm hoping someone can steer me in the right direction because right now I feel like I'm just beating my head against a wall...
The first class is called MeasuredParams, and it's got 40+ numeric fields (height, weight, waistSize, wristSize - some int, but mostly double). The second class is a statistical classifier called Classifier. It's been trained on a subset of the MeasuredParams fields. The names of the fields that the Classifier has been trained on is stored, in order, in an array called reqdFields.
What I need to do is load a new array, toClassify, with the values stored in the fields from MeasuredParams that match the field list (including order) found in reqdFields. I can make any changes necessary to the MeasuredParams class, but I'm stuck with Classifier as it is.
My brute-force approach was to get rid of the fields in MeasuredParams and use an arrayList instead, and store the field names in an Enum object to act as an index pointer. Then loop through the reqdFields list, one element at a time, and find the matching name in the Enum object to find the correct position in the arrayList. Load the value stored at that positon into toClassify, and then continue on to the next element in reqdFields.
I'm not sure how exactly I would search through the Enum object - it would be a lot easier if the field names were stored in a second arrayList. But then the index positions between the two would have to stay matched, and I'm back to using an Enum. I think. I've been running around in circles all afternoon, and I keep thinking there must be an easier way of doing it. I'm just stuck right now and can't see past what I've started.
Any help would be GREATLY appreciated. Thanks so much!
Michael
You're probably better off using a Map rather than a List, you can use the enum as the key and get the values out.
Map<YourEnumType,ValueType> map = new HashMap<YourEnumType,ValueType>();
#Tom's recommendation to use Map is the preferred approach. Here's a trivial example that constructs such a Map for use by a static lookup() method.
private enum Season {
WINTER, SPRING, SUMMER, FALL;
private static Map<String, Season> map = new HashMap<String, Season>();
static {
for (Season s : Season.values()) {
map.put(s.name(), s);
}
}
public static Season lookup(String name) {
return map.get(name);
}
}
Note that every enum type has two implicitly declared static methods:
public static E[] values();
public static E valueOf(String name);
The values() method returns an array that is handy for constructing the Map. Alternatively, the array may be searched directly. The methods are implicit; they will appear in the javadoc of your enum when it is generated.
Addendum: As suggested by #Bert F, an EnumMap may be advantageous. See Effective Java Second Edition, Item 33: Use EnumMap instead of ordinal indexing, for a compelling example of using EnumMap to associate enums.

Why not allow an external interface to provide hashCode/equals for a HashMap?

With a TreeMap it's trivial to provide a custom Comparator, thus overriding the semantics provided by Comparable objects added to the map. HashMaps however cannot be controlled in this manner; the functions providing hash values and equality checks cannot be 'side-loaded'.
I suspect it would be both easy and useful to design an interface and to retrofit this into HashMap (or a new class)? Something like this, except with better names:
interface Hasharator<T> {
int alternativeHashCode(T t);
boolean alternativeEquals(T t1, T t2);
}
class HasharatorMap<K, V> {
HasharatorMap(Hasharator<? super K> hasharator) { ... }
}
class HasharatorSet<T> {
HasharatorSet(Hasharator<? super T> hasharator) { ... }
}
The case insensitive Map problem gets a trivial solution:
new HasharatorMap(String.CASE_INSENSITIVE_EQUALITY);
Would this be doable, or can you see any fundamental problems with this approach?
Is the approach used in any existing (non-JRE) libs? (Tried google, no luck.)
EDIT: Nice workaround presented by hazzen, but I'm afraid this is the workaround I'm trying to avoid... ;)
EDIT: Changed title to no longer mention "Comparator"; I suspect this was a bit confusing.
EDIT: Accepted answer with relation to performance; would love a more specific answer!
EDIT: There is an implementation; see the accepted answer below.
EDIT: Rephrased the first sentence to indicate more clearly that it's the side-loading I'm after (and not ordering; ordering does not belong in HashMap).
.NET has this via IEqualityComparer (for a type which can compare two objects) and IEquatable (for a type which can compare itself to another instance).
In fact, I believe it was a mistake to define equality and hashcodes in java.lang.Object or System.Object at all. Equality in particular is hard to define in a way which makes sense with inheritance. I keep meaning to blog about this...
But yes, basically the idea is sound.
A bit late for you, but for future visitors, it might be worth knowing that commons-collections has an AbstractHashedMap (in 3.2.2 and with generics in 4.0). You can override these protected methods to achieve your desired behaviour:
protected int hash(Object key) { ... }
protected boolean isEqualKey(Object key1, Object key2) { ... }
protected boolean isEqualValue(Object value1, Object value2) { ... }
protected HashEntry createEntry(
HashEntry next, int hashCode, Object key, Object value) { ... }
An example implementation of such an alternative HashedMap is commons-collections' own IdentityMap (only up to 3.2.2 as Java has its own since 1.4).
This is not as powerful as providing an external "Hasharator" to a Map instance. You have to implement a new map class for every hashing strategy (composition vs. inheritance striking back...). But it's still good to know.
HashingStrategy is the concept you're looking for. It's a strategy interface that allows you to define custom implementations of equals and hashcode.
public interface HashingStrategy<E>
{
int computeHashCode(E object);
boolean equals(E object1, E object2);
}
You can't use a HashingStrategy with the built in HashSet or HashMap. GS Collections includes a java.util.Set called UnifiedSetWithHashingStrategy and a java.util.Map called UnifiedMapWithHashingStrategy.
Let's look at an example.
public class Data
{
private final int id;
public Data(int id)
{
this.id = id;
}
public int getId()
{
return id;
}
// No equals or hashcode
}
Here's how you might set up a UnifiedSetWithHashingStrategy and use it.
java.util.Set<Data> set =
new UnifiedSetWithHashingStrategy<>(HashingStrategies.fromFunction(Data::getId));
Assert.assertTrue(set.add(new Data(1)));
// contains returns true even without hashcode and equals
Assert.assertTrue(set.contains(new Data(1)));
// Second call to add() doesn't do anything and returns false
Assert.assertFalse(set.add(new Data(1)));
Why not just use a Map? UnifiedSetWithHashingStrategy uses half the memory of a UnifiedMap, and one quarter the memory of a HashMap. And sometimes you don't have a convenient key and have to create a synthetic one, like a tuple. That can waste more memory.
How do we perform lookups? Remember that Sets have contains(), but not get(). UnifiedSetWithHashingStrategy implements Pool in addition to Set, so it also implements a form of get().
Here's a simple approach to handle case-insensitive Strings.
UnifiedSetWithHashingStrategy<String> set =
new UnifiedSetWithHashingStrategy<>(HashingStrategies.fromFunction(String::toLowerCase));
set.add("ABC");
Assert.assertTrue(set.contains("ABC"));
Assert.assertTrue(set.contains("abc"));
Assert.assertFalse(set.contains("def"));
Assert.assertEquals("ABC", set.get("aBc"));
This shows off the API, but it's not appropriate for production. The problem is that the HashingStrategy constantly delegates to String.toLowerCase() which creates a bunch of garbage Strings. Here's how you can create an efficient hashing strategy for case-insensitive Strings.
public static final HashingStrategy<String> CASE_INSENSITIVE =
new HashingStrategy<String>()
{
#Override
public int computeHashCode(String string)
{
int hashCode = 0;
for (int i = 0; i < string.length(); i++)
{
hashCode = 31 * hashCode + Character.toLowerCase(string.charAt(i));
}
return hashCode;
}
#Override
public boolean equals(String string1, String string2)
{
return string1.equalsIgnoreCase(string2);
}
};
Note: I am a developer on GS collections.
Trove4j has the feature I'm after and they call it hashing strategies.
Their map has an implementation with different limitations and thus different prerequisites, so this does not implicitly mean that an implementation for Java's "native" HashMap would be feasible.
Note: As noted in all other answers, HashMaps don't have an explicit ordering. They only recognize "equality". Getting an order out of a hash-based data structure is meaningless, as each object is turned into a hash - essentially a random number.
You can always write a hash function for a class (and often times must), as long as you do it carefully. This is a hard thing to do properly because hash-based data structures rely on a random, uniform distribution of hash values. In Effective Java, there is a large amount of text devoted to properly implementing a hash method with good behaviour.
With all that being said, if you just want your hashing to ignore the case of a String, you can write a wrapper class around String for this purpose and insert those in your data structure instead.
A simple implementation:
public class LowerStringWrapper {
public LowerStringWrapper(String s) {
this.s = s;
this.lowerString = s.toLowerString();
}
// getter methods omitted
// Rely on the hashing of String, as we know it to be good.
public int hashCode() { return lowerString.hashCode(); }
// We overrode hashCode, so we MUST also override equals. It is required
// that if a.equals(b), then a.hashCode() == b.hashCode(), so we must
// restore that invariant.
public boolean equals(Object obj) {
if (obj instanceof LowerStringWrapper) {
return lowerString.equals(((LowerStringWrapper)obj).lowerString;
} else {
return lowerString.equals(obj);
}
}
private String s;
private String lowerString;
}
good question, ask josh bloch. i submitted that concept as an RFE in java 7, but it was dropped, i believe the reason was something performance related. i agree, though, should have been done.
I suspect this has not been done because it would prevent hashCode caching?
I attempted creating a generic Map solution where all keys are silently wrapped. It turned out that the wrapper would have to hold the wrapped object, the cached hashCode and a reference to the callback interface responsible for equality-checks. This is obviously not as efficient as using a wrapper class, where you'd only have to cache the original key plus one more object (see hazzens answer).
(I also bumped into a problem related to generics; the get-method accepts Object as input, so the callback interface responsible for hashing would have to perform an additional instanceof-check. Either that, or the map class would have to know the Class of its keys.)
This is an interesting idea, but it's absolutely horrendous for performance. The reason for this is quite fundamental to the idea of a hashtable: the ordering cannot be relied upon. Hashtables are very fast (constant time) because of the way in which they index elements in the table: by computing a pseudo-unique integer hash for that element and accessing that location in an array. It's literally computing a location in memory and directly storing the element.
This contrasts with a balanced binary search tree (TreeMap) which must start at the root and work its way down to the desired node every time a lookup is required. Wikipedia has some more in-depth analysis. To summarize, the efficiency of a tree map is dependent upon a consistent ordering, thus the order of the elements is predictable and sane. However, because of the performance hit imposed by the "traverse to your destination" approach, BSTs are only able to provide O(log(n)) performance. For large maps, this can be a significant performance hit.
It is possible to impose a consistent ordering on a hashtable, but to do so involves using techniques similar to LinkedHashMap and manually maintaining the ordering. Alternatively, two separate data structures can be maintained internally: a hashtable and a tree. The table can be used for lookups, while the tree can be used for iteration. The problem of course is this uses more than double the required memory. Also, insertions are only as fast as the tree: O(log(n)). Concurrent tricks can bring this down a bit, but that isn't a reliable performance optimization.
In short, your idea sounds really good, but if you actually tried to implement it, you would see that to do so would impose massive performance limitations. The final verdict is (and has been for decades): if you need performance, use a hashtable; if you need ordering and can live with degraded performance, use a balanced binary search tree. I'm afraid there's really no efficiently combining the two structures without losing some of the guarantees of one or the other.
There's such a feature in com.google.common.collect.CustomConcurrentHashMap, unfortunately, there's currently no public way how to set the Equivalence (their Hasharator). Maybe they're not yet done with it, maybe they don't consider the feature to be useful enough. Ask at the guava mailing list.
I wonder why it haven't happened yet, as it was mentioned in this talk over two years ago.

Categories