← Заметки

Новый ID в Java: порядок нормализации и усечение

Нормализация ID зависит от порядка преобразований. В частности, после усечения до 15 символов нужно снова удалить точку в конце. Удаление исходной конечной точки раньше не мешает усечению открыть другую.

Правила задачи нормализуют регистр ASCII, сохраняют допустимые символы, затем по порядку обрабатывают точки, пустой результат и длину. Оба решения предполагают гарантированный задачей вход, отличный от null. Решение с Optional использует repeat() из Java 11; оба компилируются для Java 11. Сохраните одно из решений в Solution.java, не помещая оба класса в один файл.

Ход обработки

Схема соответствует версии с Optional. Цикл объединяет фильтрацию и сжатие последовательностей точек, сохраняя те же правила.

Схема соответствует версии с Optional. Цикл объединяет фильтрацию и сжатие последовательностей точек, сохраняя те же правила.

Объединение этапов через Optional

import java.util.Locale;
import java.util.Optional;

public class Solution {
    public String solution(String new_id) {
        return Optional.of(new_id)
                .map(s -> s.toLowerCase(Locale.ROOT))
                .map(s -> s.replaceAll("[^a-z0-9._-]", ""))
                .map(s -> s.replaceAll("[.]+", "."))
                .map(s -> s.replaceAll("^[.]|[.]$", ""))
                .map(s -> s.isEmpty() ? "a" : s)
                .map(s -> s.length() > 15 ? s.substring(0, 15) : s)
                .map(s -> s.replaceAll("[.]$", ""))
                .map(s -> s.length() < 3
                        ? s + s.substring(s.length() - 1).repeat(3 - s.length()) : s)
                .orElseThrow();
    }
}

Locale.ROOT избавляет перевод в нижний регистр от зависимости от локали по умолчанию. Шаблон сохраняет только строчные ASCII-буквы, цифры, точки, подчёркивания и дефисы. Сожмите последовательности точек и удалите точки по краям. Замените пустой результат на a, усеките до 15 символов, удалите новую точку в конце и повторяйте последний символ до длины 3.

Optional — способ соединить преобразования, а не обязательная структура данных. Optional.of отвергает null, а не молча считает его пустым ID.

Те же правила с помощью цикла

Это решение объединяет фильтрацию допустимых символов и удаление последовательных точек в один проход. Преобразование только заглавных ASCII-букв использует диапазон входов задачи; это не общая нормализация имён Unicode.

import java.util.Objects;

public class Solution {
    public String solution(String new_id) {
        Objects.requireNonNull(new_id);
        StringBuilder result = new StringBuilder();
        for (char input : new_id.toCharArray()) {
            char c = input >= 'A' && input <= 'Z' ? (char) (input + 32) : input;
            boolean allowed = (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')
                    || c == '-' || c == '_' || c == '.';
            if (!allowed || (c == '.' && (result.length() == 0
                    || result.charAt(result.length() - 1) == '.'))) continue;
            result.append(c);
        }
        if (result.length() > 0 && result.charAt(result.length() - 1) == '.') {
            result.setLength(result.length() - 1);
        }
        if (result.length() == 0) result.append('a');
        if (result.length() > 15) result.setLength(15);
        if (result.charAt(result.length() - 1) == '.') result.setLength(result.length() - 1);
        while (result.length() < 3) result.append(result.charAt(result.length() - 1));
        return result.toString();
    }
}
Вход Результат Проверяемое правило
=.= aaa Подстановка и дополнение пустого результата
z-+.^. z-- Допустимые символы и удаление точки в конце
I iii Нижний регистр независимо от локали по умолчанию
abcdefghijklmn.op abcdefghijklmn Удаление точки, открытой усечением

Оба решения требуют O(n) времени по длине входа n и O(n) дополнительной памяти. Версия с регулярными выражениями может создавать несколько промежуточных строк, а цикл использует StringBuilder. Без измерений это не доказывает, что один вариант всегда быстрее. Поведение замены, перевода регистра и повторения описано в String API.

О литеральной замене названий цифр см. replace и replaceAll. О независимых от локали ключах кеша см. пример LRU-кеша.

Обсуждение

Посмотреть на GitHub ↗