Skip to content

Commit cb4a685

Browse files
authored
OPENNLP-1759: Optimize computation of hashCode in StringList (#814)
1 parent 442e2c7 commit cb4a685

File tree

2 files changed

+18
-30
lines changed

2 files changed

+18
-30
lines changed

opennlp-core/opennlp-runtime/src/main/java/opennlp/tools/util/StringList.java

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.Iterator;
2222
import java.util.NoSuchElementException;
2323
import java.util.Objects;
24+
import java.util.stream.Collectors;
2425

2526
import opennlp.tools.util.jvm.StringInterners;
2627

@@ -30,9 +31,11 @@
3031
public class StringList implements Iterable<String> {
3132

3233
private final String[] tokens;
33-
3434
private final boolean caseSensitive;
3535

36+
// It is safe to use caching of the hashCode for this class
37+
private transient Integer hashCode = null; // initial value is uncomputed
38+
3639
/**
3740
* Initializes a {@link StringList} instance. By default, this instance is case-sensitive.
3841
* <p>
@@ -53,7 +56,7 @@ public StringList(String singleToken) {
5356
*
5457
* @param tokens The string parts of the new {@link StringList}.
5558
* Must not be an empty tokens array or {@code null}.
56-
*
59+
*
5760
* @throws IllegalArgumentException Thrown if parameters were invalid.
5861
*/
5962
public StringList(String... tokens) {
@@ -73,15 +76,13 @@ public StringList(String... tokens) {
7376
* @throws IllegalArgumentException Thrown if parameters were invalid.
7477
*/
7578
public StringList(boolean isCaseSensitive, String... tokens) {
76-
7779
Objects.requireNonNull(tokens, "tokens must not be null");
7880

7981
if (tokens.length == 0) {
8082
throw new IllegalArgumentException("tokens must not be empty");
8183
}
8284

8385
this.tokens = new String[tokens.length];
84-
8586
for (int i = 0; i < tokens.length; i++) {
8687
this.tokens[i] = StringInterners.intern(tokens[i]);
8788
}
@@ -161,8 +162,11 @@ public boolean compareToIgnoreCase(StringList tokens) {
161162

162163
@Override
163164
public int hashCode() {
164-
// if lookup is too slow optimize this
165-
return StringUtil.toLowerCase(toString()).hashCode();
165+
if (hashCode == null) {
166+
// compute once and cache to safe CPU cycles during use
167+
this.hashCode = StringUtil.toLowerCase(String.join(",", tokens)).hashCode();
168+
}
169+
return hashCode;
166170
}
167171

168172
@Override
@@ -184,21 +188,7 @@ public boolean equals(Object obj) {
184188
*/
185189
@Override
186190
public String toString() {
187-
StringBuilder string = new StringBuilder();
188-
189-
string.append('[');
190-
191-
for (int i = 0; i < size(); i++) {
192-
string.append(getToken(i));
193-
194-
if (i < size() - 1) {
195-
string.append(',');
196-
}
197-
}
198-
199-
string.append(']');
200-
201-
return string.toString();
191+
return Arrays.stream(tokens).collect(Collectors.joining(",", "[", "]"));
202192
}
203193

204194
/**

opennlp-extensions/opennlp-uima/src/test/java/opennlp/uima/dictionary/DictionaryResourceTest.java

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import org.junit.jupiter.api.BeforeAll;
3939
import org.junit.jupiter.api.Test;
4040

41+
import opennlp.tools.dictionary.Dictionary;
4142
import opennlp.tools.util.StringList;
4243
import opennlp.uima.AbstractTest;
4344
import opennlp.uima.util.CasUtil;
@@ -69,16 +70,13 @@ private static AnalysisEngine produceAE(String descName)
6970
public void testDictionaryWasLoaded() {
7071

7172
try {
72-
DictionaryResource dic = (DictionaryResource) AE.getResourceManager()
73+
final DictionaryResource dic = (DictionaryResource) AE.getResourceManager()
7374
.getResource("/opennlp.uima.Dictionary");
74-
// simple check if ordering always is the same...
75-
Assertions.assertEquals(
76-
"[[Berlin], [Stockholm], [New,York], [London], [Copenhagen], [Paris]]",
77-
dic.getDictionary().toString());
78-
// else we can do a simple test like this
79-
Assertions.assertEquals(6,
80-
dic.getDictionary().asStringSet().size(), "There should be six entries in the dictionary");
81-
Assertions.assertTrue(dic.getDictionary().contains(new StringList("London")),
75+
final Dictionary d = dic.getDictionary();
76+
Assertions.assertNotNull(d);
77+
Assertions.assertEquals(6, d.asStringSet().size(),
78+
"There should be six entries in the dictionary");
79+
Assertions.assertTrue(d.contains(new StringList("London")),
8280
"London should be in the dictionary");
8381
} catch (Exception e) {
8482
Assertions.fail("Dictionary was not loaded.");

0 commit comments

Comments
 (0)