接受的答案在 99% 的情况下都有效,但问题在于细节。
具体来说,当地图有一个以前缀开头的键,然后是 Character.MAX_VALUE 后跟其他任何内容时,接受的答案不起作用。对已接受的答案发表的评论会产生一些小的改进,但仍不能涵盖所有情况。
以下解决方案还使用NavigableMap 来挑选给定键前缀的子映射。解决方案是subMapFrom() 方法,诀窍是不要增加/增加前缀的最后一个字符,而是在切断所有尾随MAX_VALUEs 的同时,不增加不是MAX_VALUE 的最后一个字符。例如,如果前缀是“abc”,我们将其递增为“abd”。但如果前缀是“ab”+MAX_VALUE,我们会删除最后一个字符并替换前一个字符,从而产生“ac”。
import static java.lang.Character.MAX_VALUE;
public class App
{
public static void main(String[] args) {
NavigableMap<String, String> map = new TreeMap<>();
String[] keys = {
"a",
"b",
"b" + MAX_VALUE,
"b" + MAX_VALUE + "any",
"c"
};
// Populate map
Stream.of(keys).forEach(k -> map.put(k, ""));
// For each key that starts with 'b', find the sub map
Stream.of(keys).filter(s -> s.startsWith("b")).forEach(p -> {
System.out.println("Looking for sub map using prefix \"" + p + "\".");
// Always returns expected sub maps with no misses
// [b, b, bany], [b, bany] and [bany]
System.out.println("My solution: " +
subMapFrom(map, p).keySet());
// WRONG! Prefix "b" misses "bany"
System.out.println("SO answer: " +
map.subMap(p, true, p + MAX_VALUE, true).keySet());
// WRONG! Prefix "b" misses "b" and "bany"
System.out.println("SO comment: " +
map.subMap(p, true, tryIncrementLastChar(p), false).keySet());
System.out.println();
});
}
private static <V> NavigableMap<String, V> subMapFrom(
NavigableMap<String, V> map, String keyPrefix)
{
final String fromKey = keyPrefix, toKey; // undefined
// Alias
String p = keyPrefix;
if (p.isEmpty()) {
// No need for a sub map
return map;
}
// ("ab" + MAX_VALUE + MAX_VALUE + ...) returns index 1
final int i = lastIndexOfNonMaxChar(p);
if (i == -1) {
// Prefix is all MAX_VALUE through and through, so grab rest of map
return map.tailMap(p, true);
}
if (i < p.length() - 1) {
// Target char for bumping is not last char; cut out the residue
// ("ab" + MAX_VALUE + MAX_VALUE + ...) becomes "ab"
p = p.substring(0, i + 1);
}
toKey = bumpChar(p, i);
return map.subMap(fromKey, true, toKey, false);
}
private static int lastIndexOfNonMaxChar(String str) {
int i = str.length();
// Walk backwards, while we have a valid index
while (--i >= 0) {
if (str.charAt(i) < MAX_VALUE) {
return i;
}
}
return -1;
}
private static String bumpChar(String str, int pos) {
assert !str.isEmpty();
assert pos >= 0 && pos < str.length();
final char c = str.charAt(pos);
assert c < MAX_VALUE;
StringBuilder b = new StringBuilder(str);
b.setCharAt(pos, (char) (c + 1));
return b.toString();
}
private static String tryIncrementLastChar(String p) {
char l = p.charAt(p.length() - 1);
return l == MAX_VALUE ?
// Last character already max, do nothing
p :
// Bump last character
p.substring(0, p.length() - 1) + ++l;
}
}
输出:
Looking for sub map using prefix "b".
My solution: [b, b, bany]
SO answer: [b, b]
SO comment: [b, b, bany]
Looking for sub map using prefix "b".
My solution: [b, bany]
SO answer: [b, bany]
SO comment: []
Looking for sub map using prefix "bany".
My solution: [bany]
SO answer: [bany]
SO comment: [bany]
也许应该补充一点,我还尝试了各种其他方法,包括我在互联网上其他地方找到的代码。所有这些都因产生不正确的结果而失败,或者由于各种异常而直接崩溃。