Как удалить одинаковые буквы из строки? - Java SE

382
25 сентября 2017, 01:35

Возник вопрос: Как удалить одинаковые буквы из строки? Есть вот это:

for(int i = 0; i < word[0].length(); i++) {
    for(int j = 1; j < word[0].length(); j++) {
        if(word[0].charAt(i) == word[0].charAt(j)) {
            word[0] = word[0].replace((word[0].charAt(i)), "");
            word[0] = word[0].replace((word[0].charAt(j)), "");
        }
    }
}

Насколько я понимаю проблема в этой строчке

word[0] = word[0].replace((word[0].charAt(i)), "");

Видимо я пытаюсь заменить char на string, поэтому и не работает. Пожалуйста, подскажите как мне реализовать эту функцию.

Пример работы, должно быть так: на входе - Java на выходе - Jv То есть должны удаляться все символы, встречающиеся более 1 раза

Answer 1
String str = "Java 8 RulezZz";
Map<Integer, Long> frequencies = str.toLowerCase()
                                    .codePoints()
                                    .boxed()
                                    .collect(Collectors.groupingBy(
                                      Function.identity(), Collectors.counting()));
String duplicates = frequencies.entrySet()
                      .stream()
                      .filter(e -> e.getValue() > 1)
                      .map(Map.Entry::getKey)
                      .map(Character::toChars)
                      .map(String::valueOf)
                      .filter(i -> !i.equals(" "))
                      .collect(Collectors.joining());
System.out.println(str.replaceAll("(?i)[" + duplicates + "]", ""));

Код в основном использует появившееся в Java 8 механизмы функционального программирования - Stream API, лямбды и ссылки на методы. Google поможет найти огромное количество вводных статей в эту область. Если хочется ознакомиться с вопросом подробнее, могу посоветовать учебник Уорбэртона "Лямбда-выражения в Java 8" или "Java SE 8 Базовый курс" Хорстманна. Попробую разобрать пример построчно:

str.codePoints() - вызов метода codePoints() возвращает поток элементов типа int. Каждый элемент представляет код символа в оригинальной строке.

Вызов boxed() упаковывает каждый int в потоке в Integer.

Вызов collect() - терминальная операция преобразующая поток во что-либо. В данном случаем мы передаём параметром предопределённый коллектор groupBy, использующийся для группировки элементов.

Первым параметром коллектора мы передаём результат вызова статического метода Function.identity(), который возвращает лямбда-выражение, в свою очередь, возвращающее без изменений всё, что ему передадут. Можно было вместо Function.identity() написать i -> i.

Вторым параметром коллектора мы передаём результат вызова статического метода Collectors.counting(), который возвращает коллектор подсчитывающий количество элементов в потоке.

В результате манипуляций с этим потоком мы получаем в переменную frequencies отображение (Map), содержащий в ключах коды символов, а в значениях количество раз, который этот код встретился в потоке:

{32=2, 97=2, 82=1, 101=1, 117=1, 118=1, 56=1, 90=1, 122=2, 74=1, 108=1}

Вызов frequencies.entrySet() возвращает множество элементов отображения, которое мы тут же преобразуем в новый поток вызовом stream().

В следующей строке - filter(e -> e.getValue() > 1) мы отфильтровываем элементы значения которых равны единицы, то есть встретились в первом потоке только один раз.

Вызов map(Map.Entry::getKey) отбрасывает значения элементов, оставляя в потоке только ключи, так как значения нам больше не нужны, мы уже использовали их для фильтрации на предыдущем шаге.

Вызов map(Character::toChars) преобразует коды символов в сами символы.

Вызов map(String::valueOf) преобразует символы в строки.

Вызов filter(i -> !i.equals(" ")) отфильтровывает пробелы. Мы же не хотим, чтобы в результате удаления дублирующихся пробелов в нашей итоговой строке все слова слиплись.

И, как и прошлый, этот поток заканчивается коллектором - .collect(Collectors.joining()), но на этот раз объединяющим отдельные строки потока в одну результирующую.

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

Вызовом метода String.replaceAll() мы разом удаляем из str все символы duplicates. Первым параметром метод получает регулярное выражение. Его первая часть - (?i) означает, что метод должен игнорировать регистр символов. Вторая часть, образующаяся в результате конкатенации - [az] представляет набор символов, которые надо заменить. Вторым параметром метода передаётся строка, на которую эти символы необходимо заменить. Так как нам надо их удалить, то заменяем мы их на пустую строку.

Или можно в императивном стиле:

Map<Character, Integer> chars = new HashMap<>();
StringBuilder sb = new StringBuilder();
for (char c : str.toLowerCase().toCharArray()) {
    if (c != ' ')
        chars.merge(c, 1, Integer::sum);
}
sb.append("(?i)[");
for (Map.Entry<Character, Integer> entry : chars.entrySet()) {
    if (entry.getValue() > 1)
        sb.append(entry.getKey());
}
sb.append("]");
System.out.println(str.replaceAll(sb.toString(), ""));
Answer 2
Set<Character> chars = new HashSet();
for(int i = 0; i<string.length(); i++){
    chars.add(string.charAt(i));
}

ideone

READ ALSO
Наглядный пример различия DTO, POCO (POJO) и Value Object

Наглядный пример различия DTO, POCO (POJO) и Value Object

Навеяно статьёй о различиях DTO, POCO и Value Object на Хабрахабре: DTO vs POCO vs Value Object, а также вопросом POCO vs DTO

295
Как открыть файл из директории assets

Как открыть файл из директории assets

Хочу передать файл soundmp3 на сервер

321
Чем отличается колбэк от слушателя?

Чем отличается колбэк от слушателя?

Например, чтобы обработать нажатие на Button необходимо воспользоваться методом setOnClickListener передав оному OnClickListener (слушатель)И такой Listener много...

240
JAVA - Можно ли создать exe файл?

JAVA - Можно ли создать exe файл?

Можно ли создать exe файл в JAVA? И можно ли будет его запрограммировать?

250