ICU-20373 simpler state saving for Java string tries via long not object
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/util/BytesTrie.java b/icu4j/main/classes/core/src/com/ibm/icu/util/BytesTrie.java
index b5e0db3..10ff5ba 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/util/BytesTrie.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/util/BytesTrie.java
@@ -87,6 +87,40 @@
     }
 
     /**
+     * Returns the state of this trie as a 64-bit integer.
+     * The state value is never 0.
+     *
+     * @return opaque state value
+     * @see #resetToState64
+     * @draft ICU 64
+     * @provisional This API might change or be removed in a future release.
+     */
+    public long getState64() {
+        return ((long)remainingMatchLength_ << 32) | pos_;
+    }
+
+    /**
+     * Resets this trie to the saved state.
+     * Unlike {@link #resetToState(State)}, the 64-bit state value
+     * must be from {@link #getState64()} from the same trie object or
+     * from one initialized the exact same way.
+     * Because of no validation, this method is faster.
+     *
+     * @param state The opaque trie state value from getState64().
+     * @return this
+     * @see #getState64
+     * @see #resetToState
+     * @see #reset
+     * @draft ICU 64
+     * @provisional This API might change or be removed in a future release.
+     */
+    public BytesTrie resetToState64(long state) {
+        remainingMatchLength_ = (int)(state >> 32);
+        pos_ = (int)state;
+        return this;
+    }
+
+    /**
      * BytesTrie state object, for saving a trie's current state
      * and resetting the trie back to this state later.
      * @stable ICU 4.8
@@ -120,6 +154,8 @@
 
     /**
      * Resets this trie to the saved state.
+     * Slower than {@link #resetToState64(long)} which does not validate the state value.
+     *
      * @param state The State object which holds a saved trie state.
      * @return this
      * @throws IllegalArgumentException if the state object contains no state,
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/util/CharsTrie.java b/icu4j/main/classes/core/src/com/ibm/icu/util/CharsTrie.java
index 8b93423..bb3f47a 100644
--- a/icu4j/main/classes/core/src/com/ibm/icu/util/CharsTrie.java
+++ b/icu4j/main/classes/core/src/com/ibm/icu/util/CharsTrie.java
@@ -90,6 +90,40 @@
     }
 
     /**
+     * Returns the state of this trie as a 64-bit integer.
+     * The state value is never 0.
+     *
+     * @return opaque state value
+     * @see #resetToState64
+     * @draft ICU 64
+     * @provisional This API might change or be removed in a future release.
+     */
+    public long getState64() {
+        return ((long)remainingMatchLength_ << 32) | pos_;
+    }
+
+    /**
+     * Resets this trie to the saved state.
+     * Unlike {@link #resetToState(State)}, the 64-bit state value
+     * must be from {@link #getState64()} from the same trie object or
+     * from one initialized the exact same way.
+     * Because of no validation, this method is faster.
+     *
+     * @param state The opaque trie state value from getState64().
+     * @return this
+     * @see #getState64
+     * @see #resetToState
+     * @see #reset
+     * @draft ICU 64
+     * @provisional This API might change or be removed in a future release.
+     */
+    public CharsTrie resetToState64(long state) {
+        remainingMatchLength_ = (int)(state >> 32);
+        pos_ = (int)state;
+        return this;
+    }
+
+    /**
      * CharsTrie state object, for saving a trie's current state
      * and resetting the trie back to this state later.
      * @stable ICU 4.8
@@ -123,6 +157,8 @@
 
     /**
      * Resets this trie to the saved state.
+     * Slower than {@link #resetToState64(long)} which does not validate the state value.
+     *
      * @param state The State object which holds a saved trie state.
      * @return this
      * @throws IllegalArgumentException if the state object contains no state,
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/BytesTrieTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/BytesTrieTest.java
index 37af016..6cca357 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/BytesTrieTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/BytesTrieTest.java
@@ -547,6 +547,7 @@
         checkFirst(trie, data, dataLength);
         checkNext(trie, data, dataLength);
         checkNextWithState(trie, data, dataLength);
+        checkNextWithState64(trie, data, dataLength);
         checkNextString(trie, data, dataLength);
         checkIterator(trie, data, dataLength);
     }
@@ -739,6 +740,54 @@
         }
     }
 
+    private void checkNextWithState64(BytesTrie trie, StringAndValue data[], int dataLength) {
+        assertNotEquals("trie(initial state).getState64()!=0", 0, trie.getState64());
+        for(int i=0; i<dataLength; ++i) {
+            byte[] expectedString=data[i].bytes;
+            int stringLength=data[i].s.length();
+            int partialLength=stringLength/3;
+            for(int j=0; j<partialLength; ++j) {
+                if(!trie.next(expectedString[j]).matches()) {
+                    errln("trie.next()=BytesTrie.Result.NO_MATCH for a prefix of "+data[i].s);
+                    return;
+                }
+            }
+            long state = trie.getState64();
+            assertNotEquals("trie.getState64()!=0", 0, state);
+            BytesTrie.Result resultAtState=trie.current();
+            BytesTrie.Result result;
+            int valueAtState=-99;
+            if(resultAtState.hasValue()) {
+                valueAtState=trie.getValue();
+            }
+            result=trie.next(0);  // mismatch
+            if(result!=BytesTrie.Result.NO_MATCH || result!=trie.current()) {
+                errln("trie.next(0) matched after part of "+data[i].s);
+            }
+            if( resultAtState!=trie.resetToState64(state).current() ||
+                (resultAtState.hasValue() && valueAtState!=trie.getValue())
+            ) {
+                errln("trie.next(part of "+data[i].s+") changes current()/getValue() after "+
+                      "saveState/next(0)/resetToState");
+            } else if(!(result=trie.next(expectedString, partialLength, stringLength)).hasValue() ||
+                      result!=trie.current()) {
+                errln("trie.next(rest of "+data[i].s+") does not seem to contain "+data[i].s+" after "+
+                      "saveState/next(0)/resetToState");
+            } else if(!(result=trie.resetToState64(state).
+                                next(expectedString, partialLength, stringLength)).hasValue() ||
+                      result!=trie.current()) {
+                errln("trie does not seem to contain "+data[i].s+
+                      " after saveState/next(rest)/resetToState");
+            } else if(trie.getValue()!=data[i].value) {
+                errln(String.format("trie value for %s is %d=0x%x instead of expected %d=0x%x",
+                                    data[i].s,
+                                    trie.getValue(), trie.getValue(),
+                                    data[i].value, data[i].value));
+            }
+            trie.reset();
+        }
+    }
+
     // next(string) is also tested in other functions,
     // but here we try to go partway through the string, and then beyond it.
     private void checkNextString(BytesTrie trie, StringAndValue data[], int dataLength) {
diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/CharsTrieTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/CharsTrieTest.java
index d153987..149e7bf 100644
--- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/CharsTrieTest.java
+++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/util/CharsTrieTest.java
@@ -674,6 +674,7 @@
         checkFirst(trie, data, dataLength);
         checkNext(trie, data, dataLength);
         checkNextWithState(trie, data, dataLength);
+        checkNextWithState64(trie, data, dataLength);
         checkNextString(trie, data, dataLength);
         checkIterator(trie, data, dataLength);
     }
@@ -885,6 +886,54 @@
         }
     }
 
+    private void checkNextWithState64(CharsTrie trie, StringAndValue[] data, int dataLength) {
+        assertNotEquals("trie(initial state).getState64()!=0", 0, trie.getState64());
+        for(int i=0; i<dataLength; ++i) {
+            String expectedString=data[i].s;
+            int stringLength=expectedString.length();
+            int partialLength=stringLength/3;
+            for(int j=0; j<partialLength; ++j) {
+                if(!trie.next(expectedString.charAt(j)).matches()) {
+                    errln("trie.next()=BytesTrie.Result.NO_MATCH for a prefix of "+data[i].s);
+                    return;
+                }
+            }
+            long state = trie.getState64();
+            assertNotEquals("trie.getState64()!=0", 0, state);
+            BytesTrie.Result resultAtState=trie.current();
+            BytesTrie.Result result;
+            int valueAtState=-99;
+            if(resultAtState.hasValue()) {
+                valueAtState=trie.getValue();
+            }
+            result=trie.next(0);  // mismatch
+            if(result!=BytesTrie.Result.NO_MATCH || result!=trie.current()) {
+                errln("trie.next(0) matched after part of "+data[i].s);
+            }
+            if( resultAtState!=trie.resetToState64(state).current() ||
+                (resultAtState.hasValue() && valueAtState!=trie.getValue())
+            ) {
+                errln("trie.next(part of "+data[i].s+") changes current()/getValue() after "+
+                      "saveState/next(0)/resetToState");
+            } else if(!(result=trie.next(expectedString, partialLength, stringLength)).hasValue() ||
+                      result!=trie.current()) {
+                errln("trie.next(rest of "+data[i].s+") does not seem to contain "+data[i].s+" after "+
+                      "saveState/next(0)/resetToState");
+            } else if(!(result=trie.resetToState64(state).
+                                next(expectedString, partialLength, stringLength)).hasValue() ||
+                      result!=trie.current()) {
+                errln("trie does not seem to contain "+data[i].s+
+                      " after saveState/next(rest)/resetToState");
+            } else if(trie.getValue()!=data[i].value) {
+                errln(String.format("trie value for %s is %d=0x%x instead of expected %d=0x%x",
+                                    data[i].s,
+                                    trie.getValue(), trie.getValue(),
+                                    data[i].value, data[i].value));
+            }
+            trie.reset();
+        }
+    }
+
     // next(string) is also tested in other functions,
     // but here we try to go partway through the string, and then beyond it.
     private void checkNextString(CharsTrie trie, StringAndValue[] data, int dataLength) {