diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index a9df1ce12250f7998aad488f0f4dccb3f7b80da7..9909638d488759760a2e646a7c289b65e0b90c0e 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -144,9 +144,7 @@ public class BinaryDictionary extends Dictionary {
         int codesSize = codes.size();
         Arrays.fill(mInputCodes, -1);
         if (codesSize > 0) {
-            int[] alternatives = codes.getCodesAt(0);
-            System.arraycopy(alternatives, 0, mInputCodes, 0,
-                    Math.min(alternatives.length, MAX_PROXIMITY_CHARS_SIZE));
+            mInputCodes[0] = codes.getCodeAt(0);
         }
 
         int count = getBigramsNative(mNativeDict, chars, chars.length, mInputCodes, codesSize,
@@ -205,11 +203,7 @@ public class BinaryDictionary extends Dictionary {
 
         Arrays.fill(mInputCodes, WordComposer.NOT_A_CODE);
         for (int i = 0; i < codesSize; i++) {
-            final int[] alternatives = codes.getCodesAt(i);
-            if (alternatives == null || alternatives.length < 1) {
-                continue;
-            }
-            mInputCodes[i] = alternatives[0];
+            mInputCodes[i] = codes.getCodeAt(i);
         }
         Arrays.fill(outputChars, (char) 0);
         Arrays.fill(scores, 0);
diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
index 8e8adc1c246ac5dcc50e25f668e829e64add0b2a..f8de029bddecdcf5de5ba15a04577b93e7ce4031 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
@@ -210,7 +210,11 @@ public class ExpandableDictionary extends Dictionary {
         if (mCodes.length < mInputLength) mCodes = new int[mInputLength][];
         // Cache the codes so that we don't have to lookup an array list
         for (int i = 0; i < mInputLength; i++) {
-            mCodes[i] = codes.getCodesAt(i);
+            // TODO: Calculate proximity info here.
+            if (mCodes[i] == null || mCodes[i].length < 1) {
+                mCodes[i] = new int[1];
+            }
+            mCodes[i][0] = codes.getCodeAt(i);
         }
         mMaxDepth = mInputLength * 3;
         getWordsRec(mRoots, codes, mWordBuilder, 0, false, 1, 0, -1, callback);
@@ -319,7 +323,7 @@ public class ExpandableDictionary extends Dictionary {
                 }
             } else {
                 // Don't use alternatives if we're looking for missing characters
-                final int alternativesSize = skipPos >= 0? 1 : currentChars.length;
+                final int alternativesSize = skipPos >= 0 ? 1 : currentChars.length;
                 for (int j = 0; j < alternativesSize; j++) {
                     final int addedAttenuation = (j > 0 ? 1 : 2);
                     final int currentChar = currentChars[j];
diff --git a/java/src/com/android/inputmethod/latin/LastComposedWord.java b/java/src/com/android/inputmethod/latin/LastComposedWord.java
index bc07924346bb3153b938695f4af2dbecf73c635d..af0ef4b3732fae12dc2c7ef4c0001eadfc1758a0 100644
--- a/java/src/com/android/inputmethod/latin/LastComposedWord.java
+++ b/java/src/com/android/inputmethod/latin/LastComposedWord.java
@@ -18,8 +18,6 @@ package com.android.inputmethod.latin;
 
 import android.text.TextUtils;
 
-import java.util.ArrayList;
-
 /**
  * This class encapsulates data about a word previously composed, but that has been
  * committed already. This is used for resuming suggestion, and cancel auto-correction.
@@ -42,7 +40,7 @@ public class LastComposedWord {
 
     public static final int NOT_A_SEPARATOR = -1;
 
-    public final ArrayList<int[]> mCodes;
+    public final int[] mPrimaryKeyCodes;
     public final int[] mXCoordinates;
     public final int[] mYCoordinates;
     public final String mTypedWord;
@@ -56,10 +54,10 @@ public class LastComposedWord {
 
     // Warning: this is using the passed objects as is and fully expects them to be
     // immutable. Do not fiddle with their contents after you passed them to this constructor.
-    public LastComposedWord(final ArrayList<int[]> codes, final int[] xCoordinates,
+    public LastComposedWord(final int[] primaryKeyCodes, final int[] xCoordinates,
             final int[] yCoordinates, final String typedWord, final String committedWord,
             final int separatorCode) {
-        mCodes = codes;
+        mPrimaryKeyCodes = primaryKeyCodes;
         mXCoordinates = xCoordinates;
         mYCoordinates = yCoordinates;
         mTypedWord = typedWord;
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index cabf68099110923603075f3d5e3b5e45d751b5c9..29a7e48167e4f708ccea1ef1f076653d76b81b17 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -21,7 +21,6 @@ import com.android.inputmethod.keyboard.KeyDetector;
 import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.keyboard.KeyboardActionListener;
 
-import java.util.ArrayList;
 import java.util.Arrays;
 
 /**
@@ -32,9 +31,9 @@ public class WordComposer {
     public static final int NOT_A_CODE = KeyDetector.NOT_A_CODE;
     public static final int NOT_A_COORDINATE = -1;
 
-    final static int N = BinaryDictionary.MAX_WORD_LENGTH;
+    private static final int N = BinaryDictionary.MAX_WORD_LENGTH;
 
-    private ArrayList<int[]> mCodes;
+    private int[] mPrimaryKeyCodes;
     private int[] mXCoordinates;
     private int[] mYCoordinates;
     private StringBuilder mTypedWord;
@@ -44,6 +43,7 @@ public class WordComposer {
     private int mCapsCount;
     private boolean mAutoCapitalized;
     private int mTrailingSingleQuotesCount;
+    private int mCodePointSize;
 
     /**
      * Whether the user chose to capitalize the first char of the word.
@@ -51,12 +51,13 @@ public class WordComposer {
     private boolean mIsFirstCharCapitalized;
 
     public WordComposer() {
-        mCodes = new ArrayList<int[]>(N);
+        mPrimaryKeyCodes = new int[N];
         mTypedWord = new StringBuilder(N);
         mXCoordinates = new int[N];
         mYCoordinates = new int[N];
         mAutoCorrection = null;
         mTrailingSingleQuotesCount = 0;
+        refreshSize();
     }
 
     public WordComposer(WordComposer source) {
@@ -64,7 +65,7 @@ public class WordComposer {
     }
 
     public void init(WordComposer source) {
-        mCodes = new ArrayList<int[]>(source.mCodes);
+        mPrimaryKeyCodes = Arrays.copyOf(source.mPrimaryKeyCodes, source.mPrimaryKeyCodes.length);
         mTypedWord = new StringBuilder(source.mTypedWord);
         mXCoordinates = Arrays.copyOf(source.mXCoordinates, source.mXCoordinates.length);
         mYCoordinates = Arrays.copyOf(source.mYCoordinates, source.mYCoordinates.length);
@@ -72,18 +73,23 @@ public class WordComposer {
         mIsFirstCharCapitalized = source.mIsFirstCharCapitalized;
         mAutoCapitalized = source.mAutoCapitalized;
         mTrailingSingleQuotesCount = source.mTrailingSingleQuotesCount;
+        refreshSize();
     }
 
     /**
      * Clear out the keys registered so far.
      */
     public void reset() {
-        mCodes.clear();
         mTypedWord.setLength(0);
         mAutoCorrection = null;
         mCapsCount = 0;
         mIsFirstCharCapitalized = false;
         mTrailingSingleQuotesCount = 0;
+        refreshSize();
+    }
+
+    public final void refreshSize() {
+        mCodePointSize = mTypedWord.codePointCount(0, mTypedWord.length());
     }
 
     /**
@@ -91,20 +97,15 @@ public class WordComposer {
      * @return the number of keystrokes
      */
     public final int size() {
-        return mCodes.size();
+        return mCodePointSize;
     }
 
     public final boolean isComposingWord() {
-        return mCodes.size() > 0;
+        return size() > 0;
     }
 
-    /**
-     * Returns the codes at a particular position in the word.
-     * @param index the position in the word
-     * @return the unicode for the pressed and surrounding keys
-     */
-    public int[] getCodesAt(int index) {
-        return mCodes.get(index);
+    public int getCodeAt(int index) {
+        return mPrimaryKeyCodes[index];
     }
 
     public int[] getXCoordinates() {
@@ -149,9 +150,10 @@ public class WordComposer {
      * @param codes the array of unicode values
      */
     private void add(int primaryCode, int[] codes, int keyX, int keyY) {
-        final int newIndex = mCodes.size();
+        final int newIndex = size();
         mTypedWord.appendCodePoint(primaryCode);
-        mCodes.add(codes);
+        refreshSize();
+        mPrimaryKeyCodes[newIndex] = codes[0];
         if (newIndex < BinaryDictionary.MAX_WORD_LENGTH) {
             mXCoordinates[newIndex] = keyX;
             mYCoordinates[newIndex] = keyY;
@@ -201,9 +203,8 @@ public class WordComposer {
      * Delete the last keystroke as a result of hitting backspace.
      */
     public void deleteLast() {
-        final int size = mCodes.size();
+        final int size = size();
         if (size > 0) {
-            mCodes.remove(size - 1);
             // Note: mTypedWord.length() and mCodes.length differ when there are surrogate pairs
             final int stringBuilderLength = mTypedWord.length();
             if (stringBuilderLength < size) {
@@ -217,9 +218,10 @@ public class WordComposer {
                 mTypedWord.deleteCharAt(stringBuilderLength - 1);
             }
             if (Character.isUpperCase(lastChar)) mCapsCount--;
+            refreshSize();
         }
         // We may have deleted the last one.
-        if (0 == mCodes.size()) {
+        if (0 == size()) {
             mIsFirstCharCapitalized = false;
         }
         if (mTrailingSingleQuotesCount > 0) {
@@ -307,29 +309,31 @@ public class WordComposer {
         // Note: currently, we come here whenever we commit a word. If it's a MANUAL_PICK
         // or a DECIDED_WORD we may cancel the commit later; otherwise, we should deactivate
         // the last composed word to ensure this does not happen.
-        final ArrayList<int[]> codes = mCodes;
+        final int[] primaryKeyCodes = mPrimaryKeyCodes;
         final int[] xCoordinates = mXCoordinates;
         final int[] yCoordinates = mYCoordinates;
-        mCodes = new ArrayList<int[]>(N);
+        mPrimaryKeyCodes = new int[N];
         mXCoordinates = new int[N];
         mYCoordinates = new int[N];
-        final LastComposedWord lastComposedWord = new LastComposedWord(codes,
+        final LastComposedWord lastComposedWord = new LastComposedWord(primaryKeyCodes,
                 xCoordinates, yCoordinates, mTypedWord.toString(), committedWord, separatorCode);
         if (type != LastComposedWord.COMMIT_TYPE_DECIDED_WORD
                 && type != LastComposedWord.COMMIT_TYPE_MANUAL_PICK) {
             lastComposedWord.deactivate();
         }
         mTypedWord.setLength(0);
+        refreshSize();
         mAutoCorrection = null;
         return lastComposedWord;
     }
 
     public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord) {
-        mCodes = lastComposedWord.mCodes;
+        mPrimaryKeyCodes = lastComposedWord.mPrimaryKeyCodes;
         mXCoordinates = lastComposedWord.mXCoordinates;
         mYCoordinates = lastComposedWord.mYCoordinates;
         mTypedWord.setLength(0);
         mTypedWord.append(lastComposedWord.mTypedWord);
+        refreshSize();
         mAutoCorrection = null; // This will be filled by the next call to updateSuggestion.
     }
 }