您可以通过使用CharBuffer 作为键类型来避免昂贵的字符串构造:
Map<CharBuffer, MyType> lookup = new TreeMap<>(Comparator
.comparingInt(CharBuffer::remaining)
.thenComparing((cb1,cb2) -> {
for(int p1 = cb1.position(), p2 = cb2.position(); p1 < cb1.limit(); p1++, p2++) {
char c1 = cb1.get(p1), c2 = cb2.get(p2);
if(c1 == c2) continue;
c1 = Character.toUpperCase(c1);
c2 = Character.toUpperCase(c2);
if(c1 != c2) return Integer.compare(c1, c2);
}
return 0;
}));
lookup.put(CharBuffer.wrap("RETURN"), MyType.KEYWORD_RETURN);
boolean lookupIdentifier(
CharBuffer buffer, int offset, int length, Map<CharBuffer, MyType> lookupTable) {
int currentPos = buffer.position(), currLimit = buffer.limit();
buffer.clear().position(offset).limit(offset + length);
boolean result = lookupTable.containsKey(buffer);
buffer.clear().position(currentPos).limit(currLimit);
return result;
}
比较器在执行不区分大小写的字符比较之前使用廉价的长度比较。这假设您使用诸如 RETURN 之类的关键字,这些关键字具有简单的大小写映射。
对于包含 50 个关键字的地图,使用 log₂ 比较进行查找可能仍会产生合理的性能。请注意,每次比较都会在第一次不匹配时停止。
您可以将散列与专用包装对象一起使用:
final class LookupKey {
final CharBuffer cb;
LookupKey(CharBuffer cb) {
this.cb = cb;
}
@Override public int hashCode() {
int code = 1;
for(int p = cb.position(); p < cb.limit(); p++) {
code = Character.toUpperCase(cb.get(p)) + code * 31;
}
return code;
}
@Override public boolean equals(Object obj) {
if(!(obj instanceof LookupKey)) return false;
final LookupKey other = (LookupKey)obj;
CharBuffer cb1 = this.cb, cb2 = other.cb;
if(cb1.remaining() != cb2.remaining()) return false;
for(int p1 = cb1.position(), p2 = cb2.position(); p1 < cb1.limit(); p1++, p2++) {
char c1 = cb1.get(p1), c2 = cb2.get(p2);
if(c1 == c2) continue;
c1 = Character.toUpperCase(c1);
c2 = Character.toUpperCase(c2);
if(c1 != c2) return false;
}
return true;
}
}
Map<LookupKey, MyType> lookup = new HashMap<>();
lookup.put(new LookupKey(CharBuffer.wrap("RETURN")), MyType.KEYWORD_RETURN);
boolean lookupIdentifier(
CharBuffer buffer, int offset, int length, Map<LookupKey, MyType> lookupTable) {
int currentPos = buffer.position(), currLimit = buffer.limit();
buffer.clear().position(offset).limit(offset + length);
boolean result = lookupTable.containsKey(new LookupKey(buffer));
buffer.clear().position(currentPos).limit(currLimit);
return result;
}
像LookupKey 这样的轻量级对象的构造,与String 构造不同,不需要复制字符内容,可以忽略不计。但请注意,与比较器不同,散列必须预先处理所有字符,这可能比小型 TreeMap 的日志 2 比较更昂贵。
如果这些关键字不太可能更改,则显式查找代码(即在键字符串的不变属性上使用 switch)会更有效。例如。从切换length 开始,如果大多数关键字的长度不同,然后切换一个对于大多数关键字都不同的字符(包括case 用于大写和小写变体的标签)。另一种选择是这些属性的分层查找结构。