Stackoverflow в Arrays.asList(str.split(«,»)) -> из-за неправильной рекурсии

#java #recursion #stack-overflow #backtracking

#java #рекурсия #переполнение стека #отслеживание возврата

Вопрос:

РЕДАКТИРОВАТЬ : операция с запятыми не была проблемой. Это была моя ошибка. Смотрите ниже.

Я написал метод для преобразования строки через запятую в список строк. Но я получил stackoverflow в этом методе.

 Exception in thread "main" java.lang.StackOverflowError
at java.util.ArrayList.subList(Unknown Source)
at java.lang.String.split(Unknown Source)
at java.lang.String.split(Unknown Source)
at de.dhbw.horb.routePlanner.SupportMethods.commaStrToStrList(SupportMethods.java:82)

public static List<String> commaStrToStrList(String commaStr) {
    if (commaStr == null)
        return null;
    return new ArrayList<String>(Arrays.asList(commaStr.split(",")));
}
  

Итак, почему это происходит? Является ли моя запятая большой? Если да, что еще можно использовать для этого?

Метод doNextNode(id) является рекурсией.: https://github.com/Spenhouet/RoutenplanerProjekt/blob/master/Routenplaner/main/de/dhbw/horb/routePlanner/parser/JDomGraphDataCreator.java

Редактировать :

Проблема была связана с отсутствующим предложением в моем методе рекурсии. Таким образом, рекурсия стала очень глубокой. Сначала я увеличил максимальный размер стека до 3 ГБ (-Xss3g), но это закончилось исключением нехватки памяти. :/

Затем я подумал, возможно ли, что моя рекурсия начинается сначала в какой-то момент и делает одно и то же снова и снова. Для этого я добавил карту, которая содержит каждый посещенный узел (id) с удалением узлов за неудачные повторные попытки (отслеживание назад).

Теперь это работает как шарм. Спасибо Marco13 за указание на то, что в конечном итоге моя рекурсия становится слишком глубокой.

Комментарии:

1. Почему вы помещаете это в новый ArrayList, если ваш возвращаемый тип — List? Не могли бы вы просто вернуть Arrays.asList ?

2. @Rod_Algonquin Что-то вроде следующего: «первый, второй, третий, четвертый»

3. @besnep это все?? больше ничего?? можете ли вы опубликовать всю строку целиком?

4. @MightyPork Я предполагаю, что OP захочет манипулировать этим списком позже, но вы не можете добавлять или удалять элементы в список, который был бы результатом Arrays.asList .

5. Ошибка StackOverflowError не обязательно означает, что рекурсия бесконечна. Это означает только, что рекурсия слишком глубокая. Глубина рекурсии 100000 вызовет SOE, даже если она ограничена этой глубиной. Был бы полезен тестовый пример, в котором проблема может быть воспроизведена (предпочтительно в виде одной (!) копии вставляемого блока кода здесь!). В противном случае: ЕСЛИ глубина рекурсии ограничена, вы можете просто попробовать начать с java -Xss2m YourApplication , чтобы увеличить размер стека до 2 МБ и посмотреть, достаточно ли этого в вашем случае.

Ответ №1:

Если ваша строка настолько велика, что String.split() разрывается на ней, у вас есть следующие варианты:

  1. Попробуйте StringTokenizer , вряд ли это будет намного лучше, но попробовать стоит. Обновление: Я проверил исходный код этого, и он выглядит довольно простым и экономичным с точки зрения памяти, эквивалентным варианту 3.
  2. Используйте необработанное регулярное выражение Matcher и создайте свой список вручную.
  3. Выполните весь синтаксический анализ вручную: выполните итерацию по символам, и если вы нажмете на запятую, создайте строку с символами между последней запятой и текущей и добавьте ее в список.

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

Комментарии:

1. Mh похоже, мне придется подумать о другом решении. Исключение в потоке «main» java.lang. Ошибка StackOverflowError в java.lang. String.indexOf(неизвестный источник) в java.util. StringTokenizer.scanToken(неизвестный источник) в java.util. StringTokenizer.nextToken(неизвестный источник) …

2. Теперь это странно, потому что StringTokenizer определенно не используется рекурсия, глубина стека вызовов не будет зависеть от длины сканируемой строки. Вы уверены, что где-нибудь в вызывающем коде нет безудержной рекурсии? Можете ли вы показать еще несколько строк из трассировки стека?

3. @biziclop Насколько я вижу, даже не ясно, задействована ли какая-то рекурсия в оригинальной программе. Когда опубликованный метод находится в «нижней части» глубокого рекурсивного вызова, фактическая строка , в которой возникает ошибка, в основном случайна…

4. @Marco13 в исходной программе задействована рекурсия. Ссылка на проект git в сообщении.

5. Тогда я подозреваю, что SOE вызван слишком глубокой рекурсией в вызывающем коде, и это просто совпадение, что когда у вас заканчивается стек, вы случайно вызываете реализацию разделения строк.