Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

HashMap: containsKey() Not true when it should be?

I am currently throwing an obfuscation program for school homework together. I am trying to make the program read a file and then create a new file that replaces each letter in the file with some a corresponding value that I pull from a HashMap. I set up a whole bunch of keys and values, but later in the process of writing the new file I try to check if the map contains a key before appending to the new String. The characters that are checked are in fact in the file that I am reading from to test, and the file is read correctly. Yet it fails with the encryptionDict.containsKey() (my hashmap) method.

I hope some Java expert can help me figure this out! I'm pretty clueless, I'm more of a C and D guy. The only thought that struck me was that it would be something like with Strings where "foo" != "foo". But chars aren't objects.

The code is in a pastebin below, the key parts to look at is the class constructor, the method encrypt, and the method initDictionary, also can someone tell me why HashMap<char, String> is invalid, is it because I have to use an object?

The code: http://pastebin.com/NcHTHPfw

like image 245
BitPuffin Avatar asked Sep 11 '12 13:09

BitPuffin


2 Answers

 private HashMap<char [], String> initDictionary() {
                HashMap<char [], String> d = new HashMap<char [], String>();

                d.put("a".toCharArray(), "!\"#¤");
                d.put("b".toCharArray(), "¤#\"!");
                d.put("c".toCharArray(), "\"#¤%");
                d.put("d".toCharArray(), "%¤#\"");
                d.put("e".toCharArray(), "#¤%&");
                d.put("f".toCharArray(), "&%¤#");
                d.put("g".toCharArray(), "¤%&/");
                d.put("h".toCharArray(), "/&%¤");
                d.put("i".toCharArray(), "%&/(");
                d.put("j".toCharArray(), "(/&%");
                d.put("k".toCharArray(), "&/()");
                d.put("l".toCharArray(), ")(/&");
                d.put("m".toCharArray(), "/()=");
                d.put("n".toCharArray(), "=)(/");
                d.put("o".toCharArray(), "()=?");
                d.put("p".toCharArray(), "?=)(");
                d.put("q".toCharArray(), ")=?!");
                d.put("r".toCharArray(), "!?=)");
                d.put("s".toCharArray(), "=?!\"");
                d.put("t".toCharArray(), "\"!?=");
                d.put("u".toCharArray(), "?!\"#");
                d.put("v".toCharArray(), "#\"!?");
                d.put("w".toCharArray(), ";:*^");
                d.put("x".toCharArray(), "^*:;");
                d.put("y".toCharArray(), ":*^>");
                d.put("z".toCharArray(), ">^*:");
// etc.

This is the problematic bit. You can't use arrays as hash keys in Java, as Array does not override the equals() and hashCode() methods.

The hashCode is used to find the correct bucket that contains the object you are looking for, and the equals() method compares the actual Objects. To make use of a HashMap, you need to override both of these methods in a sensible way, which you can't as array classes are final. So the only thing you could do if you absolutely insist on using char arrays is to use a wrapper class as key that has a char array.

Something like this:

public class Key {
    private final char[] array;

    public Key(final String string) {
        this(string.toCharArray());
    }
    public Key(final char[] array) {
        this.array = array;
    }

    public char[] getArray() {
        return array;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Key key = (Key) o;

        if (!Arrays.equals(array, key.array)) return false;

        return true;
    }

    @Override
    public int hashCode() {
        return array != null ? Arrays.hashCode(array) : 0;
    }
}

Now you can have a Map<Key, String> and bould a Key Object from either a String or a char[]

like image 163
Sean Patrick Floyd Avatar answered Sep 20 '22 23:09

Sean Patrick Floyd


The problem is that all char[] are unique, regardless of their contents. It appears you really want to be using a String. Normally, you can write such a program without using a char[]

Instead you can write

private Map<Character, String> initDictionary() {
Map<Character, String> d = new HashMap<Character, String>();

d.put('a', "!\"#¤");
// etc
like image 30
Peter Lawrey Avatar answered Sep 22 '22 23:09

Peter Lawrey