Поиск строк в TreeSet, которые начинаются с заданного префикса

Я пытаюсь найти строки в TreeSet<String>, которые начинаются с заданного префикса. Я нашел предыдущий вопрос о том же самом поиск запись в TreeSet на лету, но приведенный там ответ не работает для меня, потому что он предполагает, что строки не включают Character.MAX_VALUE, а мой может.

(Ответ заключается в использовании treeSet.subSet(prefix, prefix + Character.MAX_VALUE), что дает все строки между prefix (включительно) и prefix + Character.MAX_VALUE (исключительно), что получается для всех строк, начинающихся с prefix, кроме тех, которые начинаются с prefix + Character.MAX_VALUE. Но в В моем случае мне нужно найти все строки, начинающиеся с prefix, включая те, которые начинаются с prefix + Character.MAX_VALUE.)

Как я могу это сделать?


person Deploymental    schedule 24.05.2017    source источник
comment
Что такое тип cityNames? Не могли бы вы опубликовать минимальный, полный и проверяемый пример?   -  person tnas    schedule 24.05.2017


Ответы (2)


Для начала предлагаю пересмотреть ваши требования. Character.MAX_VALUE — это U+FFFF, который не является допустимым символом Unicode и никогда им не будет; поэтому я не могу придумать вескую причину, по которой вам нужно было бы его поддержать.

Но если для этого требования есть веская причина, вам нужно «увеличить» свой префикс, чтобы вычислить наименьшую строку, которая больше, чем все строки, начинающиеся с вашего префикса. Например, учитывая "city", вам нужно "citz". Вы можете сделать это следующим образом:

/**
 * @param prefix
 * @return The least string that's greater than all strings starting with
 *         prefix, if one exists. Otherwise, returns Optional.empty().
 *         (Specifically, returns Optional.empty() if the prefix is the
 *         empty string, or is just a sequence of Character.MAX_VALUE-s.)
 */
private static Optional<String> incrementPrefix(final String prefix) {
    final StringBuilder sb = new StringBuilder(prefix);

    // remove any trailing occurrences of Character.MAX_VALUE:
    while (sb.length() > 0 && sb.charAt(sb.length() - 1) == Character.MAX_VALUE) {
        sb.setLength(sb.length() - 1);
    }

    // if the prefix is empty, then there's no upper bound:
    if (sb.length() == 0) {
        return Optional.empty();
    }

    // otherwise, increment the last character and return the result:
    sb.setCharAt(sb.length() - 1, (char) (sb.charAt(sb.length() - 1) + 1));
    return Optional.of(sb.toString());
}

Чтобы использовать его, вам нужно использовать subSet, когда вышеуказанный метод возвращает строку, и tailSet, когда он ничего не возвращает:

/**
 * @param allElements - a SortedSet of strings. This set must use the
 *                      natural string ordering; otherwise this method
 *                      may not behave as intended.
 * @param prefix
 * @return The subset of allElements containing the strings that start
 *         with prefix.
 */
private static SortedSet<String> getElementsWithPrefix(
        final SortedSet<String> allElements, final String prefix) {

    final Optional<String> endpoint = incrementPrefix(prefix);

    if (endpoint.isPresent()) {
        return allElements.subSet(prefix, endpoint.get());
    } else {
        return allElements.tailSet(prefix);
    }
}

Посмотрите его в действии: http://ideone.com/YvO4b3.

person ruakh    schedule 24.05.2017
comment
Спасибо большое, ты мой спаситель - person Deploymental; 25.05.2017
comment
Причина в требованиях профессора, может быть для более глубокого понимания древовидной структуры, я не знаю - person Deploymental; 25.05.2017

Если кто-то ищет более короткую версию ответа руаха:

Первый элемент на самом деле set.ceiling(prefix), а последний - вам нужно увеличить префикс и использовать set.floor(next_prefix)

public NavigableSet<String> subSetWithPrefix(NavigableSet<String> set, String prefix) {
    String first = set.ceiling(prefix);
    char[] chars = prefix.toCharArray();
    if(chars.length>0)
        chars[chars.length-1] = (char) (chars[chars.length-1]+1);
    String last = set.floor(new String(chars));
    if(first==null || last==null || last.compareTo(first)<0)
        return new TreeSet<>();
    return set.subSet(first, true, last, true);
}
person Alexey    schedule 06.09.2019
comment
Это неправильно, когда prefix пусто или заканчивается Character.MAX_VALUE; и это излишне сложно для случаев, которые он поддерживает. (И зачем кому-то искать более короткую версию ответа руаха?) - person ruakh; 07.09.2019