NullPointerException при удалении тройного слова

Ниже приведен код. Индекс массива представляет маленькие символы (a-z), а индекс равен 26 (количество символов в английском алфавите). Это словарь слов, в котором дочерний элемент [знак ascii-значение-97] указывает на следующий узел. Концу слова присваивается bool terminal=true.

Все функции рекурсивны. В функции удаления мы должны пройти до конца слова символ за символом. Во время обхода при втором вызове рекурсивного удаления я теряю все свои ссылки и происходит NullPointerException.

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

import java.io.File;
import java.util.Scanner;

public class xxx {

public static void main(String[] args) {
    Trie trie = new Trie();

                if (trie.delete(word)) {
                    System.out.println("Word deleted");
                } else {
                    System.out.println("Word not present");
                }
                break;
            }
            case "S": { //Search for the word
                String word = tokens[1];
                if (trie.isPresent(word)) {
                    System.out.println("Word found");
                } else {
                    System.out.println("Word not found");
                }
}

Этот класс просто вызывает рекурсивные функции класса Node. Класс trie получает вызов от основного класса, а затем передает данные рекурсивным функциям в классе Node.

class Trie {

Node root;

public Trie() {
    root = new Node();

}

boolean isPresent(String s) { // returns true if s is present, false otherwise
    current = root;
    for (int i = 0; i < s.length(); i++) {
        if (current.children[(int) s.charAt(i) - 97] == null) {
            return false;
        } else {
            current = current.children[(int) s.charAt(i) - 97];
        }

    }
    if (current.terminal == false) {
        return false;
    }

    return true;
}

boolean delete(String s) { // returns false if s is not present, true otherwise
    if (!isPresent(s)) {
        return false;
    }
    root.delete(root,s);
    return true;
}

int membership() { // returns the number of words in the data structure
    return root.membership(root, 0);
}

void listAll() { // list all members of the Trie in alphabetical orber
    root.listAll(root, "");

}

}

Дети [значение ascii-97] будут ссылаться на узел, и эта ссылка будет представлять буквенный символ. OutDegree позаботится о том, чтобы была удалена только данная строка. Этот класс имеет все рекурсивные функции.

 class Node {

boolean terminal;
int outDegree;
Node[] children;

public Node() {
    terminal = false;
    outDegree = 0;
    children = new Node[26];
}



public void delete(Node x, String s) {
    if (s.length() > 1){
        if(i<s.length())
        delete(children[s.charAt(0)-97],s.substring(1)); //this is where problem occurs

    }
    else if(children[((int)s.charAt(0))-97].outDegree>0)
        terminal =false;
    else if(children[((int)s.charAt(0))-97].outDegree==0){
        children[((int)s.charAt(0))-97]=null;
        return;
    }
    if(children[s.charAt(0)-97].outDegree==0)
        children[s.charAt(0)-97]=null;
    }

}

person Fazeel    schedule 05.03.2015    source источник
comment
возможный дубликат Что такое нулевой указатель Исключение и как его исправить?   -  person gknicker    schedule 05.03.2015
comment
Я знаю, как возникают исключения nullPointerException. Мой код работает для первой рекурсии, и отладка показывает, что в массиве children[] есть хотя бы один ненулевой элемент.   -  person Fazeel    schedule 05.03.2015


Ответы (1)


Ваша проблема не в строке кода, которую вы прокомментировали. Ваша проблема в том, как вы инициализировали массив:

children = new Node[26];

Эта строка кода выделяет память массива. Однако значение каждого отдельного элемента массива устанавливается равным NULL для ссылок на объекты, символу Unicode NULL для примитивов char, false для примитивов boolean и 0 для числовых примитивов. Если вы хотите, чтобы это работало, вы должны правильно инициализировать массив.

person hfontanez    schedule 05.03.2015
comment
Инициализация массива так же хороша, как вставка слов(), список слов(), присутствует(), все эти функции работают отлично. Все ссылки изначально установлены равными нулю, и если я добавлю слово, то дети [значение ascii символа-97] будут ссылаться на новый узел. PS. Узел не содержит символ, это индекс массива, представляющий символ. - person Fazeel; 05.03.2015
comment
Первое изображение для ввода: ссылка Второе изображение показывает конфигурацию дерева для строки abcd: ссылка Третье изображение показывает первую рекурсию: ссылка четвертое изображение, где при второй рекурсии ссылка становится нулевой: ссылка< /а> - person Fazeel; 05.03.2015
comment
Очевидно, что инициализация не годится, если вы получаете NPE в этой строке. Я предлагаю вам запустить отладку и выполнить код. У вас есть этот код в вашем методе isPresent: if (current.children[(int) s.charAt(i) - 97] == null) { return false;} Который говорит мне, что вы ожидаете, что некоторые элементы массива будут NULL. Я не вижу никакого кода для решения этой проблемы. - person hfontanez; 05.03.2015
comment
Все элементы в массиве пусты, за исключением индексов, представляющих символы. Если во всем дереве есть только одно слово, то ожидается, что только один элемент в дочернем массиве будет ссылаться на следующий элемент. Другая ссылка будет нулевой. Этот патерн продолжается и в следующих узлах. - person Fazeel; 05.03.2015
comment
@Fazeel, если это так, вы явно обращаетесь к неправильному индексу. NPE — это индикатор того, что индекс, к которому вы пытаетесь получить доступ, содержит нулевую ссылку. Итак, если правильная инициализация ВСЕХ элементов массива невозможна, я предлагаю вам отладить вашу функцию доступа (current.children[(int) s.charAt(i) - 97]). Это два ваших варианта. - person hfontanez; 05.03.2015
comment
Я понял ошибку. Я вызываю рекурсивное удаление: delete(children[s.charAt(0)-97],s.substring(1)); что неправильно. Я должен вызвать его, используя ссылочную переменную, и правильный вызов: children[s.charAt(0)-97].delete(children[s.charAt(0)-97],s.substring(1)); - person Fazeel; 06.03.2015