आपको यह परिभाषित करके शुरू करना चाहिए कि पेड़ क्या है (डोमेन के लिए), यह सबसे पहले इंटरफ़ेस को परिभाषित करके किया जाता है । सभी पेड़ों की संरचनाएं संशोधित करने योग्य नहीं हैं, नोड्स को जोड़ने और हटाने में सक्षम होना एक वैकल्पिक विशेषता होनी चाहिए, इसलिए हम इसके लिए एक अतिरिक्त इंटरफ़ेस बनाते हैं।
नोड ऑब्जेक्ट्स बनाने की कोई आवश्यकता नहीं है जो मूल्यों को धारण करते हैं , वास्तव में मैं इसे एक प्रमुख डिजाइन दोष और अधिकांश पेड़ कार्यान्वयन में ओवरहेड के रूप में देखता हूं। यदि आप स्विंग को देखते हैं, तो TreeModel
नोड वर्गों से मुक्त है (केवल DefaultTreeModel
उपयोग करता है TreeNode
), क्योंकि वे वास्तव में आवश्यक नहीं हैं।
public interface Tree <N extends Serializable> extends Serializable {
List<N> getRoots ();
N getParent (N node);
List<N> getChildren (N node);
}
म्यूटेबल ट्री स्ट्रक्चर (नोड्स जोड़ने और हटाने की अनुमति देता है):
public interface MutableTree <N extends Serializable> extends Tree<N> {
boolean add (N parent, N node);
boolean remove (N node, boolean cascade);
}
इन इंटरफेस को देखते हुए, पेड़ों का उपयोग करने वाले कोड को इस बात की ज्यादा परवाह नहीं है कि पेड़ कैसे लागू किया जाता है। यह आपको जेनेरिक कार्यान्वयन के साथ-साथ विशिष्ट लोगों का उपयोग करने की अनुमति देता है, जहां आप किसी अन्य एपीआई के कार्यों को सौंपकर पेड़ का एहसास करते हैं।
उदाहरण: फ़ाइल ट्री संरचना
public class FileTree implements Tree<File> {
@Override
public List<File> getRoots() {
return Arrays.stream(File.listRoots()).collect(Collectors.toList());
}
@Override
public File getParent(File node) {
return node.getParentFile();
}
@Override
public List<File> getChildren(File node) {
if (node.isDirectory()) {
File[] children = node.listFiles();
if (children != null) {
return Arrays.stream(children).collect(Collectors.toList());
}
}
return Collections.emptyList();
}
}
उदाहरण: जेनेरिक ट्री संरचना (माता-पिता / बाल संबंधों पर आधारित):
public class MappedTreeStructure<N extends Serializable> implements MutableTree<N> {
public static void main(String[] args) {
MutableTree<String> tree = new MappedTreeStructure<>();
tree.add("A", "B");
tree.add("A", "C");
tree.add("C", "D");
tree.add("E", "A");
System.out.println(tree);
}
private final Map<N, N> nodeParent = new HashMap<>();
private final LinkedHashSet<N> nodeList = new LinkedHashSet<>();
private void checkNotNull(N node, String parameterName) {
if (node == null)
throw new IllegalArgumentException(parameterName + " must not be null");
}
@Override
public boolean add(N parent, N node) {
checkNotNull(parent, "parent");
checkNotNull(node, "node");
// check for cycles
N current = parent;
do {
if (node.equals(current)) {
throw new IllegalArgumentException(" node must not be the same or an ancestor of the parent");
}
} while ((current = getParent(current)) != null);
boolean added = nodeList.add(node);
nodeList.add(parent);
nodeParent.put(node, parent);
return added;
}
@Override
public boolean remove(N node, boolean cascade) {
checkNotNull(node, "node");
if (!nodeList.contains(node)) {
return false;
}
if (cascade) {
for (N child : getChildren(node)) {
remove(child, true);
}
} else {
for (N child : getChildren(node)) {
nodeParent.remove(child);
}
}
nodeList.remove(node);
return true;
}
@Override
public List<N> getRoots() {
return getChildren(null);
}
@Override
public N getParent(N node) {
checkNotNull(node, "node");
return nodeParent.get(node);
}
@Override
public List<N> getChildren(N node) {
List<N> children = new LinkedList<>();
for (N n : nodeList) {
N parent = nodeParent.get(n);
if (node == null && parent == null) {
children.add(n);
} else if (node != null && parent != null && parent.equals(node)) {
children.add(n);
}
}
return children;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
dumpNodeStructure(builder, null, "- ");
return builder.toString();
}
private void dumpNodeStructure(StringBuilder builder, N node, String prefix) {
if (node != null) {
builder.append(prefix);
builder.append(node.toString());
builder.append('\n');
prefix = " " + prefix;
}
for (N child : getChildren(node)) {
dumpNodeStructure(builder, child, prefix);
}
}
}