0

I am having performance problems due to the fact that in order to retrieve the value for some key, I have to check whether each key stored in the map is equal to my test key. The performance problem stems from the fact that I had to override the map's get operation which checks if the map has the test key. I need some kind of tuples for my keys and I use String[] to store two Strings. Here is my class with a map.

public class ClassWithMap {

    Map<String[], Double> arrayToDoubleMap;

    public ClassWithMap() {
        arrayToDoubleMap = new HashMap<String[], Double>() {
            @Override
            public Double get(Object key) {
                String[] stringArray = (String[]) key;

                Set<String[]> keySet = this.keySet();
                for (String[] tuple : keySet) {
                    if (tuple[0].equals(stringArray[0]) && tuple[1].equals(stringArray[1])) {
                        key = tuple;
                        break;
                    }
                }

                return super.get(key);
            }
        };
    }

    public Double getDouble(String string1, String string2) {

        String[] tuple = { string1, string2 };
        return (Double) arrayToDoubleMap.get(tuple);

    }
}

Here is some method for testing.

public static void main(String[] args) {

    ClassWithMap map = new ClassWithMap();
    String[] tuple1 = { "foo", "bar" };
    map.arrayToDoubleMap.put(tuple1, 0.0);

    String[] tuple2 = { "fee", "fum" };
    map.arrayToDoubleMap.put(tuple2, 1.0);

    System.out.println(map.getDouble("fee", "fum"));

}

If I do not override the get operation in the map declaration, I get null because the String[] key is not exactly the same as the test String[] key.

So my question is: is there a more efficient way to impose this object equivalence than to make a method which checks if there is a match between every key and test key?

under_the_sea_salad
  • 1,754
  • 3
  • 22
  • 42
  • You can make a different class, call it StringTuple, that is an object holding two strings. You can implement `compareTo()` / `equals()` by comparing string equality. – Alejandro Jul 23 '14 at 14:54
  • 1
    You shouldn't override core functionality like that (it's bound to lead to sadness). Instead, wrap your `String[]` in a simple class that implements `equals()` and `hashcode()` appropriately. – Oliver Charlesworth Jul 23 '14 at 14:55
  • @OliCharlesworth, so why is it okay to override `equals()` in this new class, but not okay to override `get()` in this map class? How do you define "core functionality"? – under_the_sea_salad Jul 23 '14 at 15:17
  • @ijkilchenko: Good question. I guess the answer is "because HashMap.get is not designed to be overridden". There may be internal implementation details that rely on `get` working in a particular way, which you may now have violated. On the other hand, `equals` is very much designed to be overriden. You could then argue that `HashMap.get` should be marked `final`, and I would agree. – Oliver Charlesworth Jul 23 '14 at 15:25
  • @OliCharlesworth, this isn't necessarily something I would dwell on, but if you look at my override again, it only changes what object is then passed into the `super.get()` method. So I wouldn't be violating much in the end. – under_the_sea_salad Jul 23 '14 at 15:52

1 Answers1

2

You should probably create a class to hold the two strings instead of putting them in an array:

class StringPair {
    private final String str1, str2;

    public StringPair(final String str1, final String str2) {
        this.str1 = str1;
        this.str2 = str2;
    }

    public String getStr1() {
        return str1;
    }

    public String getStr2() {
        return str2;
    }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof StringPair))
            return false;

        StringPair sp = (StringPair) o;
        return Objects.equals(str1, sp.str1) && Objects.equals(str2, sp.str2);
    }

    @Override
    public int hashCode() {
        return Arrays.asList(str1, str2).hashCode();
    }
}

Now, you would use a Map<StringPair, Double>.

arshajii
  • 127,459
  • 24
  • 238
  • 287
  • as far as I know, explicit casting is not advised or safe. :) – Kick Buttowski Jul 23 '14 at 15:01
  • @KickButtowski Why do you say that? – arshajii Jul 23 '14 at 15:03
  • I just shared what I have been told to see others opinion. the only reason that I can think is loss precision and I am sure other issues that I can not remember right now. if you pay attention, I said as far as I know , so it does not mean I am right or wrong. ;) – Kick Buttowski Jul 23 '14 at 15:05
  • 1
    @KickButtowski There's *no* problem with casting, and we really have no other choice in `equals`. I was just curious what led you to believe that casting is bad practice... – arshajii Jul 23 '14 at 15:05
  • 1
    @KickButtowski, there should be no problem with casting there because he checks if the object is an instance of `StringPair`. Only objects which are instances of `StringPair` will reach the casting statement. – under_the_sea_salad Jul 23 '14 at 15:13