在Java中更改对象引用

在Java中更改对象引用,java,object,Java,Object,我试图在Java中实现一个后缀trie。trie有一个根节点,与之相连的是边。但是,当实现诸如constructTrie(T)(构造一个给定字符串T的trie)或substring(S,T)(检查S是否是T的子字符串)之类的函数时,我会保留一个当前节点cNode,该节点在整个代码中根据我考虑的节点而变化 我不确定是否正确更改了cNode的值。下面是classTrie import java.util.*; 三类{ 受保护的节点根=null; 公共图书馆{ 节点n=新节点(); 根=n; } //

我试图在Java中实现一个后缀trie。trie有一个根节点,与之相连的是边。但是,当实现诸如
constructTrie(T)
(构造一个给定字符串T的trie)或
substring(S,T)
(检查S是否是T的子字符串)之类的函数时,我会保留一个当前节点
cNode
,该节点在整个代码中根据我考虑的节点而变化

我不确定是否正确更改了
cNode
的值。下面是class
Trie

import java.util.*;
三类{
受保护的节点根=null;
公共图书馆{
节点n=新节点();
根=n;
}
//为给定字符串T构造一个trie
公共void构造函数(字符串T){
ArrayList后缀数组=新的ArrayList();
T+=“#”;//终止符
int length=T.length();
//创建后缀数组并在每次迭代中删除第一个字母

for(int i=0;i更新类的方法中的字段会使类不具有线程安全性。您的方法具有的副作用可能不是类用户所期望的

考虑:

 Trie t = new Trie("My String");

 boolean startsWithMy = t.substring("My");
 boolean startsWithMyString = t.substring("My String");
如果您正在更新substring方法中的
root
字段,那么第二次调用将不会执行预期的操作,因为第一次子字符串调用更改了Trie

如果您想创建一个易于使用且副作用最小的可重用类,那么我将按照以下基本模式编写您的类:

public class Trie {
     private final Node root;

     public Trie(String input) {
         // Construct the Trie here and assign it to root:
         this.root = constructTry(input);
     }

     public boolean substring(String part) {
         // Create a local Node variable:
         Node currentNode = root;

         // Navigate the Trie here using currentNode:
         // ...

         return result;
     }
}
您甚至可以添加一个方法(如果需要)来返回Trie的子部分:

public Trie subTrie(String part) {
    // Find the Node here that matches the substring part, and return it.
    // If nothing found, then throw NoSuchElementException or return null.

    Node subNode = findNode(part);

    if (subNode == null) {
        throw new NoSuchElementException("No element starting with: " + part);
    }

    // Constructs a new Trie with a different root node using a 2nd constructor option
    return new Trie(subNode); 
}

您正在通过向根节点添加垃圾来更改其引用。 假设你这样做:

 Trie trie = new Trie();
 trie.substring("es", "pest"); // this returns true. 
但如果你这样做了

 Trie trie = new Trie();
 trie.substring("es", "pest");  
 trie.substring("te", "Master"); 
对Substring的第二次调用将拾取上次调用留下的位置。您的根已经初始化,并且包含一个单词“pest”的树
根(p,e,s,t,#)
。在第二次调用后,您没有像预期的那样具有
根(M,a,s,t,e,r,#)
,而是以
根(p,e,s,t,#,M,a,r)结束
。这是一个完全不同的词。因此,
te
不是
pest\Mar
的子串

但如果您根据@john16384实施,您将被迫执行以下操作,以消除副作用:

 Trie trie = new Trie("pest");
 trie.substring("es"); // this returns true. 

 trie = new Trie("Master");
 trie.substring("te") // this returns true. 
这样做总是会迫使您从一个干净的根目录开始。请参阅下面的实现:

 class Trie {

protected Node root = null;

public Trie(String T) {
    root = constructTrie(T);
}

// Constructs a trie for a given string T
private Node constructTrie(String T) {
    ArrayList<String> suffixArray = new ArrayList<String>();
    T += "#"; // Terminator
    int length = T.length();
    // Creates suffix array and removes first letter with every iteration
    for (int i = 0; i < length; i++) {
        suffixArray.add(T);
        T = T.substring(1);
    }
    Node localRoot = new Node();
    // Goes through suffix array
    for (int i = 0; i < length; i++) {
        Node cNode = localRoot;
        int j = 0;
        // Goes through each letter of an entry in the suffix array
        while (j < (suffixArray.get(i)).length()) {
            int index = cNode.findEdge((suffixArray.get(i)).charAt(j));
            // If an edge is found at the root with the current letter, update cNode and remove the letter from word
            if (index != -1) {
                cNode = cNode.getEdge(index).getNode(); // Gets node pointed at by edge and sets it as current node
                String replace = (suffixArray.get(i)).substring(1);
                suffixArray.set(0, replace); // Erases first letter of suffix
                j++;
                System.out.println(i + " " + j + " " + replace);
            }
            // If an edge is not found at the root, write the whole word
            else {
                for (int k = 0; k < (suffixArray.get(i)).length(); k++) {
                    Edge e = new Edge((suffixArray.get(i)).charAt(k)); // Creates edge with current letter of current entry of the suffix array
                    Node n = new Node(); // Creates node to be pointed at by edge
                    e.setNode(n);
                    cNode.newEdge(e);
                    cNode = n; // Updates current node
                }
                j = (suffixArray.get(i)).length(); // If the word is written, we break from the while and move on to the next suffix array entry
            }
        }
    }
    return localRoot;
}

// Checks if S is a substring of T
public boolean substring(String S) {
    Node cNode = root;
    int index;
    for (int i = 0; i < S.length(); i++) {
        index = cNode.findEdge(S.charAt(i));
        if (index == -1)
            return false; // Substring was not found because a path was not followed
        cNode = (cNode.getEdge(index)).getNode(); // Reset current node to the next node in the path
    }
    return true; // Substring was found
}
}
class-Trie{
受保护的节点根=null;
公共Trie(字符串T){
root=constructrie(T);
}
//为给定字符串T构造一个trie
私有节点构造函数(字符串T){
ArrayList后缀数组=新的ArrayList();
T+=“#”;//终止符
int length=T.length();
//创建后缀数组并在每次迭代中删除第一个字母
for(int i=0;i
如果将“trie”替换为“tree”,您可能会得到更多答案,这就是我猜你的意思。@Mena实际上,首先我们必须实现一个trie,然后将其更改为一个树以优化其性能。我希望它实际上是一个树,而不仅仅是一个树。@QuoVadis我输出了
suffixArray
,它正在做我想做的事情,为字符串创建一个后缀数组,例如,对于“pest”,数组将是pest,est#,st#,t#,#。是的,您是对的,您正在将t重新分配给自身。因此无需随光标移动。这意味着
constructrie
必须返回一个节点,对吗?但是我如何在方法中引用“本地”根而不是全局根节点?它将返回一个节点?是的。我不确定您对globa的意思是什么l根节点。constructTrie方法应该创建“根”节点并返回该节点。它还应该创建由根节点指向的子节点。我这样做了,现在看起来好多了。事实证明,在类
节点
中定义函数时,我的错误是非常愚蠢的