views:

298

answers:

4

Currently I'm using a HashMap to map the strings to int and the int values need to be frequently accessed. I'm looking for a better way to do this if possible with minimal object creation, and preferable being able to store the values as primitive ints without wrapping them with the Integer class. (Basically, the reverse of the SparseArray's int->object mapping.)

A: 

Are they arbitrary values known at compile time? In that case, use a Java Enum or an Android Enum.

Randolpho
The int value will be a mask consisting of 1 or more of these: 0x01, 0x02, 0x04, 0x08 and 0x10, maximum value is 31.
Al
A: 

If you're using a HashMap there will be no way around using some object. If you're max value is 31 all values will be cached by the Integer implementation. There will be no object createion as long as you're using autoboxing or Integer.valueOf to access the Integer instances (not new Integer(..)).

If you want to minimize object creation you could write an mutable wrapper around a primitive int. One alternative is to (mis-)use java.util.concurrent.atomic.AtomicInteger which has some overhead.

Thomas Jung
+2  A: 

Use String[32]. The index is your int value. Hold null for any slot in the array where you have no corresponding String.

EDIT: Sorry, this is for an int->String lookup. For String->int, you can still use String[32], but you'd need to store them in sorted order and use Arrays#binarySearch() or something. I really think you should just stick with the HashMap.

CommonsWare
I think that in this particular case your solution is the most efficient.... one point up
Ramps
I **could** be the most efficient. If the strings are long and not internalized it won't work that well. It's O(n * m) (n number of elements, m string length). A map has ideally O(1).
Thomas Jung
I do not believe Java arrays are stored as you think. I believe String[] is a n*4-byte set of handles to the String objects. Your model assumes a String[] is somehow a combination of one huge buffer *and* a separate array of offsets into that huge buffer, and that would not work in Java -- the Strings in the String[] have to be addressable as separate objects.
CommonsWare
@commonsware.com: Don't get it. I'm only saying your code will do: `String[] xs = new Strings[32]; for(int i=0;i<32;i++) if(string.equals(x)) return i;` and String.equals will iterate over the content char[] of the Strings which can take some time. It may still be faster for short or internalized strings as the HashMap has some overhead and collissions are possible.
Thomas Jung
*smacks forehead* I have been misreading the OP. I thought it was int->String lookups, not String->int. Mea culpa!
CommonsWare
A: 

Write a class like this:

public class Foo
{
    public static final String A = "a";
    public static final String B = "b";

    public static int foo(String str)
    {
        final int val;

        if(str == A || str.equals(A))
        {
            val = 0x01;
        }
        else if(str == B || str.equals(B))
        {
            val = 0x02;
        }
        // etc...

        return (val);
    }
}

You only create the Strings once each, the number of Strings are small enough that the .equals won't get called many times, and if you always us ethe constants then the .equals won't get called at all.

If you use a Map it will take more memory than this, and also given that the number of Strings is small the code above might be faster. Also you can refactor the implementation to use a Map internally (or an array, or whatever) and see what is the fastest/uses the least memory without having to change your API.

EDIT:

Another thing to look at is the proposal for switch with String... if this comes into the language (I think it is) and if Android adopts it, then you would be able to replace the code I have above with a switch without a performance hit. Essentially they do a switch on the hashCode and then only call .equals on objects that have the same hashCode. This does require that you figure out the hashCodes in advance, and that the hashCode always returns the same thing for ever (which it should since String defines the way hashCode must work).

TofuBeer
Actually, the switch will never call equals on a String. The compiler while produce a perfect hash function (no collisions) for all strings in the switch (http://blogs.sun.com/darcy/entry/string_unhashing).
Thomas Jung
An efficient (and hard to read) implementation could be implemented the same way: `switch(hash(str)){ case HASH_VALUE_X : ... }`. This will produce better byte code (http://java.sun.com/docs/books/jvms/second_edition/html/Compiling.doc.html#14942) than the if/else alternative.
Thomas Jung
cool... but since it isn't in the language yet and you could wind up with collisions now you would either have to backport hte idea of the perfect hash function for the strings in the switch or do wah I suggest to get this to work... :-)
TofuBeer