5
\$\begingroup\$

Is there a more optimal way of retrieving the character for the letter? I would assume that I would just replace my Entry set loop with another map, but I do not want to bloat the code, because it will reduce scalability.

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

public class AciiLetters {
    private enum Letter {
        _A("010101111101101"), _B("110101110101110"), _C("011100100100011"),
        _D("110101101101110"), _E("111100110100111"), _F("111100110100100"),
        _G("011100101101011"), _H("101101111101101"), _I("111010010010111"),
        _J("011001001101010"), _K("101101110101101"), _L("100100100100111"),
        _M("101111111101101"), _N("111101101101101"), _O("010101101101010"),
        _P("110101110100100"), _Q("010101101011001"), _R("110101110101101"),
        _S("011100010001110"), _T("111010010010010"), _U("101101101101111"),
        _V("101101101101010"), _W("101101111111101"), _X("101101010101101"),
        _Y("101101010010010"), _Z("111001010100111"), _0("111101101101111"),
        _1("010110010010111"), _2("111001111100111"), _3("111001011001111"),
        _4("101101111001001"), _5("111100111001111"), _6("111100111101111"),
        _7("111001001001001"), _8("111101111101111"), _9("111101111001111"),
        SPACE("000000000000000"), UNKNOWN("010101001010010");

        private static final Map<Character, Letter> cache;
        static {
            cache = new HashMap<Character, Letter>();

            cache.put('A', _A); cache.put('B', _B); cache.put('C', _C);
            cache.put('D', _D); cache.put('E', _E); cache.put('F', _F);
            cache.put('G', _G); cache.put('H', _H); cache.put('I', _I);
            cache.put('J', _J); cache.put('K', _K); cache.put('L', _L);
            cache.put('M', _M); cache.put('N', _N); cache.put('O', _O);
            cache.put('P', _P); cache.put('Q', _Q); cache.put('R', _R);
            cache.put('S', _S); cache.put('T', _T); cache.put('U', _U);
            cache.put('V', _V); cache.put('W', _W); cache.put('X', _X);
            cache.put('Y', _Y); cache.put('Z', _Z); cache.put('0', _0);
            cache.put('1', _1); cache.put('2', _2); cache.put('3', _3);
            cache.put('4', _4); cache.put('5', _5); cache.put('6', _6);
            cache.put('7', _7); cache.put('8', _8); cache.put('9', _9);
            cache.put(' ', SPACE); cache.put('?', UNKNOWN);
        }

        public static final int WIDTH = 3, HEIGHT = 5;
        private String bitSequence;

        Letter(String bitSequence) {
            this.bitSequence = bitSequence;
        }

        public static Letter getEnum(Character value) {
            Letter letter = cache.get(value);

            if (letter != null)
                return letter;

            return UNKNOWN;
        }

        public char getChar() {
            for (Entry<Character, Letter> entry : cache.entrySet()) {
                if (entry.getValue() == this)
                    return entry.getKey();
            }

            return '?';
        }
    }

    public AciiLetters() {
        printText("ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789 ?", "  ");
    }

    public void printText(String text, String spacing) {
        Letter letter;
        int offset;
        String seq;
        Character val;

        for (int row = 0; row < Letter.HEIGHT; row++) {
            for (Character tCh : text.toCharArray()) {
                letter = Letter.getEnum(tCh);
                offset = row * Letter.WIDTH;
                seq = letter.bitSequence.substring(offset, offset + Letter.WIDTH);
                val = letter.getChar();
                for (Character ch : seq.toCharArray()) {
                    System.out.printf("%c", ch == '0' ? ' ' : val);
                }
                System.out.print(spacing);
            }
            System.out.println();
        }
    }

    public static void main(String[] args) {
        new AciiLetters();
    }
}

Output:

 A   BB    CC  DD   EEE  FFF   GG  H H  III   JJ  K K  L    M M  NNN   O   PP    Q   RR    SS  TTT  U U  V V  W W  X X  Y Y  ZZZ       000   1   222  333  4 4  555  666  777  888  999        ?   
A A  B B  C    D D  E    F    G    H H   I     J  K K  L    MMM  N N  O O  P P  Q Q  R R  S     T   U U  V V  W W  X X  Y Y    Z       0 0  11     2    3  4 4  5    6      7  8 8  9 9       ? ?  
AAA  BB   C    D D  EE   FF   G G  HHH   I     J  KK   L    MMM  N N  O O  PP   Q Q  RR    S    T   U U  V V  WWW   X    Y    Z        0 0   1   222   33  444  555  666    7  888  999         ?  
A A  B B  C    D D  E    F    G G  H H   I   J J  K K  L    M M  N N  O O  P     QQ  R R    S   T   U U  V V  WWW  X X   Y   Z         0 0   1   2      3    4    5  6 6    7  8 8    9        ?   
A A  BB    CC  DD   EEE  F     GG  H H  III   J   K K  LLL  M M  N N   O   P      Q  R R  SS    T   UUU   V   W W  X X   Y   ZZZ       000  111  222  333    4  555  666    7  888  999        ?   
\$\endgroup\$
1
  • \$\begingroup\$ BTW: I see nothing that is ASCII specific here. Your solution appears to be character set agnostic - which is good. \$\endgroup\$
    – chux
    Commented Dec 15, 2013 at 4:06

1 Answer 1

3
\$\begingroup\$

This is one of those occasions where a 'sparse' dataset may be very helpful.

But, first some nit-picks:

  • You should probably call the class AsciiLetters instead of AciiLetters
  • Declaring multiple constants on one line is unconventinal, and makes things hard to read... fix:

    public static final int WIDTH = 3, HEIGHT = 5;
    
  • In the printText you over-cook the lookups to the Enum:

    for (int row = 0; row < Letter.HEIGHT; row++) {
        for (Character tCh : text.toCharArray()) {
            letter = Letter.getEnum(tCh);
    

    this can become:

    char[] textchars = text.toCharArray();
    Letter[] letters = new Letter[textchars.length];
    for (int i = 0; i < textchars.length; i++) {
        letters[i] = Letter.getEnum(textchars[i]);
    }
    for (int row = 0; row < Letter.HEIGHT; row++) {
        for (letter letter : letters) {
    
  • also in the printText(), there is no need to do all the work with Character. Using char is better.

  • also, declaring all your variables outside the loop is not useful for anything, and can even make performance worse. Languages like C need that, but Java is better if you declare your variables when you need them, not before.
  • If you wanted to, you could store the 0 and 1 values as actual bits. This would actually be faster (slightly) but would also be more complicated. In this case, I don't think it is significantly different.

OK, now for the real issue, the storage of the Enums in a convenient-to-access system...

First, I recommend that you change your Enum to have two parameters in the constructor:

    private String bitSequence;
    Letter(String bitSequence) {
        this.bitSequence = bitSequence;
    }

This should become (note, I have also made them final!):

    private final String bitSequence;
    private final char mychar;
    Letter(char c, String bitSequence) {
        this.mychar = c;
        this.bitSequence = bitSequence;
    }

Then change each of your Enum values to also send the char it represents.

If you do that, then your getChar() method becomes simply:

    public char getChar() {
        return mychar;
    }

BUT, I was lazy, and I pulled your code in to my eclipse environment, and I could not be bothered to change all the Enum values... so I cheated... and used:

    public char getChar() {
        return name().charAt(1);
    }

That takes the second letter from the Enum name (e.g. it will pull L from _L). Since all your Enum names have a systematic name scheme, this will work, but, it's not the best system...

But still, once I have the getChar() working off the internal values of the enum (instead of the Map<...>), you can do the following:

replace the line:

private static final Map<Character, Letter> cache;

and instead have:

// store enough values for ASCII characters. If we wanted to, even 32768 is not large
// and with that we could store **all** Letters,
private static final Letter[] cache = new Letter[128];

Then, in your static block, fill the cache with 'Unknown', and then 'fix' the ones you know:

    static {
        // assume things are UNKNOWN.
        Arrays.fill(cache, UNKNOWN);
        // 'fix' the things we actually know...
        for (Letter letter : values()) {
            // use the actual char (cast implicitly to an int...)
            // as the index to the array.
            // if you have chars >= 128 you will need to make the array bigger.
            cache[letter.getChar()] = letter;
        }
    }

Finally, using this array is really simple:

    public static Letter getEnum(char value) {
        return value < cache.length ? cache[value] : UNKNOWN;
    }

Note, I have changed the parameter to simple char instead of Character.

Putting it all together, I have your code running as (without the additional char as a constructor to the Enum...):

import java.util.Arrays;

public class AsciiLetters {
    private enum Letter {
        _A("010101111101101"), _B("110101110101110"), _C("011100100100011"),
        _D("110101101101110"), _E("111100110100111"), _F("111100110100100"),
        _G("011100101101011"), _H("101101111101101"), _I("111010010010111"),
        _J("011001001101010"), _K("101101110101101"), _L("100100100100111"),
        _M("101111111101101"), _N("111101101101101"), _O("010101101101010"),
        _P("110101110100100"), _Q("010101101011001"), _R("110101110101101"),
        _S("011100010001110"), _T("111010010010010"), _U("101101101101111"),
        _V("101101101101010"), _W("101101111111101"), _X("101101010101101"),
        _Y("101101010010010"), _Z("111001010100111"), _0("111101101101111"),
        _1("010110010010111"), _2("111001111100111"), _3("111001011001111"),
        _4("101101111001001"), _5("111100111001111"), _6("111100111101111"),
        _7("111001001001001"), _8("111101111101111"), _9("111101111001111"),
        SPACE("000000000000000"), UNKNOWN("010101001010010");

        public static final int WIDTH = 3;
        public static final int HEIGHT = 5;

        private static final Letter[] cache = new Letter[128];

        static {
            Arrays.fill(cache, UNKNOWN);
            for (Letter letter : values()) {
                cache[letter.getChar()] = letter;
            }
        }

        private String bitSequence;

        Letter(String bitSequence) {
            this.bitSequence = bitSequence;
        }

        public static Letter getEnum(char value) {
            return value < cache.length ? cache[value] : UNKNOWN;
        }

        public char getChar() {
            return name().charAt(1);
        }
    }

    public AciiLetters() {
        printText("ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789 ?", "  ");
    }

    public void printText(String text, String spacing) {

        char[] textchars = text.toCharArray();
        Letter[] letters = new Letter[textchars.length];
        for (int i = 0; i < textchars.length; i++) {
            letters[i] = Letter.getEnum(textchars[i]);
        }
        for (int row = 0; row < Letter.HEIGHT; row++) {
            for (Letter letter : letters) {
                int offset = row * Letter.WIDTH;
                String seq = letter.bitSequence.substring(offset, offset + Letter.WIDTH);
                char val = letter.getChar();
                for (Character ch : seq.toCharArray()) {
                    System.out.printf("%c", ch == '0' ? ' ' : val);
                }
                System.out.print(spacing);
            }
            System.out.println();
        }
    }

    public static void main(String[] args) {
        new AciiLetters();
    }
}
\$\endgroup\$
2
  • \$\begingroup\$ Thanks, very thorough. I will have to check this in the morning. Note: I didn't realize I spelled ASCII wrong... That was dumb on my part. I may not even have to store the char for each sequence, because I could just get the current letter that was read in from the inputText... \$\endgroup\$ Commented Dec 15, 2013 at 6:37
  • 1
    \$\begingroup\$ return name().charAt(1); now this is lazy wait for it genius. \$\endgroup\$ Commented Dec 15, 2013 at 10:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.