Заведомо верный EQUALS возвращает FALSE, почему так происходит?

Задача: есть строка, нужно изъять из неё (строки) все слова, начинающиеся на "a" и поместить в массив.

Как я решил поступить:

  1. Заменить все знаки препинания, символы на пустоту и двойные пробелы на один пробел, чтобы я мог использовать .split()
  2. Собственно использовал .split(" ") и сразу помещал его результат в ArrayList
  3. Затем я хотел перебрать эту коллекцию и нужные печатать, а ненужные — удалять, но наткнулся на исключение:

Exception in thread "main" java.util.ConcurrentModificationException

(в будущем понял, что мог только удалять, и ошибки бы не было, но я уже хочу решить другим способом)

  1. Погуглил --> наткнулся на Iterator и теперь хочу решить именно через него для лучшего понимания Java
  2. Создал Iterator на основе ArrayList — далее хочу через while перебрать итератор и, если первый символ "a", то печатать слово — иначе удаляю слово из итератора.

Но тут и начинаются проблемы:

if ("a".equals(String.valueOf(a.charAt(0)))) {, где a это:

String a = String.valueOf(iterator.next());

возвращает false, хз почему...

При этом я затем решил вывести вслед за этим циклов все элементы итератора, и у меня не вывелось ни одного, что, в принципе, логично, так как условие возвратило ложь, и всё поудалялось, но затем я по приколу ввёл индекс не 0, а 1, и у меня вывелись все слова итератора на 1 цикле, но на 2 цикле у меня всё так же не вывелось ни одного слова, как будто в предыдущем цикле их всех удалило, чего не могло быть, так как выполнилась правда.

import java.util.Arrays;
import java.util.Iterator;
import java.util.ArrayList;

public class Main {

    public static void main(String[] args) {
        test1();
    }

    public static void test1() {
        String text = "aajiwi aiwefhfhwe wagw, gerhbdf - aiwrhgwbdb wfgw. Drgbibv awhgbwwb: gfuihwergb rg ywrgyf.";
        text = text.replaceAll("[,^%$#@!^:;'/\"&*()?><\\-.]", "").replaceAll("  ", " ");
        ArrayList<String> res = new ArrayList<>();
        res.add(Arrays.toString(text.split(" ")));
        System.out.println(res); // Задача из строки изъять все слова начинающиеся с "a" и поместить в массив
        Iterator iterator = res.iterator();
        while (iterator.hasNext()) {
            String a = String.valueOf(iterator.next());
            if ("a".equals(String.valueOf(a.charAt(0)))) { 
                System.out.println(a);
            } else {
                iterator.remove();
            }
        }
        while (iterator.hasNext()) {
            System.out.println("AAAAAAAAA chto za fygniya");
        }
    }
}

Вывод, если индекс 0:

[[aajiwi, aiwefhfhwe, wagw, gerhbdf, aiwrhgwbdb, wfgw, Drgbibv, awhgbwwb, gfuihwergb, rg, ywrgyf]]
    
Process finished with exit code 0

Вывод, если индекс 1:

[[aajiwi, aiwefhfhwe, wagw, gerhbdf, aiwrhgwbdb, wfgw, Drgbibv, awhgbwwb, gfuihwergb, rg, ywrgyf]]
    
[aajiwi, aiwefhfhwe, wagw, gerhbdf, aiwrhgwbdb, wfgw, Drgbibv, awhgbwwb, gfuihwergb, rg, ywrgyf]

Process finished with exit code 0

Почему if возвращает ложь?


Ответы (2 шт):

Автор решения: talex moved to Codidact

Чтобы добавить массив строк в список нужно использовать:

res.addAll(List.of(text.split(" ")));
→ Ссылка
Автор решения: Nowhere Man

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

Главная проблема в представленном коде в строке:

res.add(Arrays.toString(text.split(" ")));
System.out.println(res);
// [[aajiwi, aiwefhfhwe, wagw, gerhbdf, aiwrhgwbdb, wfgw, Drgbibv, awhgbwwb, gfuihwergb, rg, ywrgyf]]

так как она записывает в список res одну-единственную строку как результат вызова Arrays.toString(text.split(" ")), то есть строковое представление массива строк целиком:
"[aajiwi, aiwefhfhwe, wagw, gerhbdf, aiwrhgwbdb, wfgw, Drgbibv, awhgbwwb, gfuihwergb, rg, ywrgyf]"

Разумеется, первым символом такой строки является '[', а вторым 'a', и в дальнейшем итератор по списку res обработает только эту единственную строку.

Поэтому прежде всего следует корректно записать массив строк в список res, например, посредством метода Collections::addAll(Collection<? super T> c, T ... elements):

ArrayList<String> res = new ArrayList<>();
Collections.addAll(res, text.split(" "));

или же использовать метод Arrays.asList:

List<String> res = new ArrayList<>(Arrays.asList(text.split(" ")));

И после этого следует переписать часть с итератором: сделать его типизированным, убрать ненужные преобразования в строки при проверке первого символа, использовать String::startsWith

Iterator<String> iterator = res.iterator();
while (iterator.hasNext()) {
    String word = iterator.next();
    if (word.startsWith("a")) {
        System.out.println(word);
    } else {
        iterator.remove();
    }
}
System.out.println(res); // [aajiwi, aiwefhfhwe, aiwrhgwbdb, awhgbwwb]

И если нужно всё-таки в результате получить массив слов как указано в условии задачи, то придётся преобразовать полученный список res:

String[] result = res.toArray(new String[0]);

Также следует отметить, что повторный цикл по итератору с паническим сообщением:
while (iterator.hasNext()) { System.out.println("УЖОС!");}
не имеет смысла сам по себе, так как итератор завершил работу в предыдущем цикле, но если бы вдруг было иначе, то этот цикл был бы бесконечным.


Возможные альтернативные способы решения основной задачи:

  • С использованием регулярного выражения для поиска слов в строке Matcher::find:
Pattern startWithA = Pattern.compile("\\ba\\p{L}*");

List<String> res = new ArrayList<>();
Matcher matcher = startWithA.matcher(text);
while (matcher.find()) {
    res.add(matcher.group());
}

где \\ba\\p{L}* описывает следующий формат слова: граница слова \\b, первая буква слова английская a, после которой идут 0 или более других букв \\p{L}*

  • С использованием Stream API и улучшенного регулярного выражения для разбиения слов text.split("\\P{L}+") -- разделителями являются 1 и более любых небуквенных символов:
String[] result = Arrays.stream(text.split("\\P{L}+"))
    .filter(w -> w.startsWith("a"))
    .toArray(String[]::new);
→ Ссылка