This question already has answers here:
How to implement a tree data-structure in Java?
(27 answers)
Closed 1 year ago.
I recently started college with no previous coding experience. I began to study a tree data structure and its Java implementation. The question is quite basic but I have not found an answer to it.
As in a list you'd start by typing import java.util.List what do you need to do in order to start with a tree implementation?
There is no need to import a Java package for implementing Trees. Tree structure is a collection of nodes/instance of a class referencing to the address of the child nodes. Although, there are different types of trees like Binary Search Tree or AVL Trees, however each tree has separate property.
Sample Implementation of Binary Search Tree in Java is as follows:
/* Node Class containing left and right child of current
node and key value*/
class Node
{
int key;
Node left, right;
public Node(int item)
{
key = item;
left = right = null;
}
}
class Tree
{
Node root;
Tree()
{
root = null;
}
public static void main(String[] args)
{
Tree tree = new Tree();
tree.root = new Node(1);
tree.root.left = new Node(2);
tree.root.right = new Node(3);
tree.root.left.left = new Node(4);
}
}
The easiest answer is the DefaultTreeModel class. Basically, you create TreeNode objects and add them to the Model or Parent node as needed. DefaultMutableTreeNode should be good enough to meet your needs as a beginner.
You can navigate the model using recursion easily enough.
For a regular binary tree you could implement your own:
class Node {
int value;
Node left;
Node right;
public Node(int value) {
this.value = value;
}
}
Then create a BinaryTree class that contains a root Node instance.
a tree structure is a bit different from the list implementation...
i recommend you to see this topic if you just want the code, if your focus question isn't how to do if but how does they work, i recommend this.
and if you have found the problem by using this data structure, you need to learn this
Here's my current problem:
I have a directory structure stored inside a cloud storage somewhere. Under the Root folder, I have 1000+ subdirectories and each of those have a single subdirectory under them. And within each of those subdirectories, a single file exists. So a simplified diagram looks something like this:
Root
________________|________________
| | | |
FolderA FolderB ... FolderY FolderZ
| | | |
Folder1 Folder2 Folder3 Folder4
| | | |
FileA FileB FileC FileD
For each node, it has properties type ("directory" or "file") and path ("/Root/FolderB"). And the only way to retrieve these nodes is to call a method called listDirectory(path) which goes to the cloud, gets all the objects within that path. I need to find all the files and process them.
The problem is that with the way that it's structured, if I want to look for FileA, I need to call listDirectory() three times (Root -> FolderA -> Folder1) which you can imagine slows the whole thing down significantly.
I want to process this in a parallel manner but I can't seem to get this to work. I've tried doing it recursively by using GParsPool.withPool with eachParallel() but I found out that parallel programming with recursion can be a dangerous (and expensive) slope. I've tried doing it linearly by creating a synchronized list that holds all the paths that are of directories that each thread have visited. But none of these seems to work or provide an efficient solution to this problem.
FYI, I can't change the listDirectory() method. Each call will retrieve all the objects in that path.
TL;DR: I need to find a parallel way to process through a cloud-storage file structure where the only way to get the folders/files are through a listDirectory(path) method.
If caching the directory structure in memory by using a deamon is not an option.
or caching the directory structure by initially creating a one time mapping of the storage structure in the memory and hooking into each add remove update operation to the storage and changing the database accordingly is not an option.
assuming the storage structure is a Tree (usually is) because the way listDirectory() works i think you are better off using Breadth first search to search the storage structure tree. that way you can search one level at time using parallel programming
your code could look something like this:
SearchElement.java - represents either a directory or a file
public class SearchElement {
private String path;
private String name;
public SearchElement(String path, String name) {
this.path = path;
this.name = name;
}
public String getPath() {
return path;
}
public String getName() {
return name;
}
}
ElementFinder.java - a class that searches the storage you need to replace the listDirectory function to your implementation
import java.util.ArrayList;
import java.util.Collection;
import java.util.Optional;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.atomic.AtomicReference;
public class ElementFinder {
private final SearchElement ROOT_DIRECTORY_PATH = new SearchElement("/", "");
public Optional<SearchElement> find(String elementName) {
Queue<SearchElement> currentLevelElements = new ConcurrentLinkedQueue();
currentLevelElements.add(ROOT_DIRECTORY_PATH);
AtomicReference<Optional<SearchElement>> wantedElement = new AtomicReference<>(Optional.empty());
while (!currentLevelElements.isEmpty() && wantedElement.get().isEmpty()) {
Queue<SearchElement> nextLevelElements = new ConcurrentLinkedQueue();
currentLevelElements.parallelStream().forEach(currentSearchElement -> {
Collection<SearchElement> subDirectoriesAndFiles = listDirectory(currentSearchElement.getPath());
subDirectoriesAndFiles.stream()
.filter(searchElement -> searchElement.getName().equals(elementName))
.findAny()
.ifPresent(element -> wantedElement.set(Optional.of(element)));
nextLevelElements.addAll(subDirectoriesAndFiles);
});
currentLevelElements = nextLevelElements;
}
return wantedElement.get();
}
private Collection<SearchElement> listDirectory(String path) {
return new ArrayList<>(); // replace me!
}
}
Say I constructed an Integer Node like so:
Node<Integer> node = new Node<Integer>(0, new Node<Integer>(1, new Node<Integer>(2, null)));
And get a node that looks like "0 ---> 1 ---> 2".
My objective is that when I run a command like node = node * 4 + 28; , node will look like "28 ---> 32 ---> 36", WITHOUT using functions such as multiply(...).
That is, node = node * 4 + 28 and not node.multiply(4); node.add(28); (unless it's used as a private function in a larger parsing function - a function I'd like to avoid if possible.)
Is there a way to enumerate nodes and other data structures (like Queue and Stack) like that in Java?
(Small note: Node itself is generic. It doesn't have to work with Strings, though. It'd be really nice if it did, though.)
Thanks in advance!
For those interested, Node looks like this:
public class Node<T> {
private T info;
private Node<T> next;
...
}
public class Node {
private int value = 0;
private Node next = null;
...
public void multiply(int num) {
value *= num;
if (next != null)
next.multiply(num);
}
}
If I get the question right, the function you're looking for is usually called map and it's not present in Java itself.
However, it's a paradigm commonly used in functional programming, so you might want to check e.g. Guava which offers a bunch of functional features to Java users.
private function openAllNodes(event:TimerEvent):void {
//Alert.show(event.target.currentCount);
var index:int =event.target.currentCount - 1;
myTree.openItems = treeData..node[index] ;
}
public function start_timer():void
{
timer.start();
}
private function closeAllNodes():void {
myTree.openItems = [];
}
public var timer:Timer = new Timer(1000,19);
public function init():void
{
timer.addEventListener(TimerEvent.TIMER, openAllNodes);
}
In my project I want to show the video like growing flex tree. So I tried flex timer based to expand node one by one. But if I open the first node then automatically close second time. If there is any other way to expand nodes one by one then let me know.
If you could post your dataProvider it will be easier to determine the best approach for your problem. Of the top of my head I think you could use the method expandItem() and pass in the the item using for example (#id==index).
I iterate through a tree structure to collect the paths of the leaf nodes. Which way do you prefer to collect the result of the operation:
a) merge the results of the children and return this
private Collection<String> extractPaths(final Element element, final IPath parentPath) {
final IPath path = parentPath.append(element.getLabel());
final Collection<Element> children = getElementChildren(element);
if (children.isEmpty())
return Collections.singletonList(path.toString());
final Set<String> result = new TreeSet<String>();
for (final Element child : children)
result.addAll(extractPaths(child, path));
return result;
}
b) provide the result collection as a parameter and add new elements in each recursion step
private void extractPaths(final Element element, final IPath parentPath, final Set<String> result) {
final IPath path = parentPath.append(element.getLabel());
final Collection<Element> children = getElementChildren(element);
if (children.isEmpty())
result.add(path.toString());
for (final Element child : children)
extractPaths(child, path, result);
}
Both can be used without any problems. Though, former solution is more clean since it doesn't change input parameters. No side effects is in the nature of functional programming.
I assume the latter is meant to call extractPaths(child, path, result)?
The latter form will be more efficient, as it doesn't need to copy items at every level of recursion. As Boris says, it's less functionally clean - but Java doesn't really provide immutable collections with appropriate methods to create new collections based on them efficiently.
In terms of making it pleasant to call, you could provide a wrapper in the style of the first option which just creates a new set and calls the second option. That's probably what I'd do:
private Collection<String> extractPaths(Element element, IPath parentPath) {
Set<String> ret = new HashSet<String>();
extractPaths(element, parentPath, ret);
return ret;
}
Another alternative is to change the third parameter from a Set<String> to some sort of "collector" interface: you tell it that you've found a result, without specifying what to do with it. Indeed, the collector could return a new collector to use from then on - leaving it up to the implementation to decide whether to make a functionally-clean "create a new set" version, or hide side-effects in the collector which would just return itself again for reuse.
To provide the most convenient and flexible interface to your clients, write it as a class that implements Iterator<E>.
This means that the client can loop through the items found during the recursion, but they don't have to implement their "for each" code as a callback (Java doesn't have a pretty way to do that), and they can even "pause" the operation and continue it later, outside of the scope in which they began it (or abandon it at any point).
It's the trickiest to implement though. If the data structure you're traversing is a tree-like structure with parent pointers in each node then you need no other data than the current node. To get to the next node, look for a first child. If there is one, that's the next node. Otherwise try the next sibling. If there isn't one, get the parent and try to get its next sibling, and so on until you hit a null in which case there are no more items.
As a quick and dirty example, here's a treenode-like class, breaking all the rules about encapsulation to save some space here:
class SimpleNode
{
String name;
public SimpleNode parent, firstChild, nextSibling;
public SimpleNode(String n) { name = n; }
public void add(SimpleNode c)
{
c.parent = this;
c.nextSibling = firstChild;
firstChild = c;
}
public String getIndent()
{
StringBuffer i = new StringBuffer();
for (SimpleNode n = this; n != null; n = n.parent)
i.append(" ");
return i.toString();
}
}
Now let's create a tree from it:
SimpleNode root = new SimpleNode("root");
SimpleNode fruit = new SimpleNode("fruit");
root.add(fruit);
fruit.add(new SimpleNode("pear"));
fruit.add(new SimpleNode("banana"));
fruit.add(new SimpleNode("apple"));
SimpleNode companies = new SimpleNode("companies");
root.add(companies);
companies.add(new SimpleNode("apple"));
companies.add(new SimpleNode("sun"));
companies.add(new SimpleNode("microsoft"));
SimpleNode colours = new SimpleNode("colours");
root.add(colours);
colours.add(new SimpleNode("orange"));
colours.add(new SimpleNode("red"));
colours.add(new SimpleNode("blue"));
Now, to spell this out for anyone new to this idea, what we want to be able to do is this:
for (final SimpleNode n : new SimpleNodeIterator(root))
System.out.println(n.getIndent() + "- " + n.name);
And get this (I've made the above code generate something that looks like a hierarchical bullet list in SO):
root
colours
blue
red
orange
companies
microsoft
sun
apple
fruit
apple
banana
pear
To do this, we have to map some standard operations onto our SimpleNode class:
class SimpleNodeIterator extends TreeIterator<SimpleNode>
{
public SimpleNodeIterator(SimpleNode root)
{ super(root); }
protected SimpleNode getFirstChild(SimpleNode of)
{ return of.firstChild; }
protected SimpleNode getNextSibling(SimpleNode of)
{ return of.nextSibling; }
protected SimpleNode getParent(SimpleNode of)
{ return of.parent; }
}
And finally, at the bottom of our design, TreeIterator<TNode> is a very reusable abstract base class that does the rest, now we've told it how to navigate our node class:
abstract class TreeIterator<TNode> implements Iterator<TNode>,
Iterable<TNode>
{
private TNode _next;
protected TreeIterator(TNode root)
{ _next = root; }
public Iterator<TNode> iterator()
{ return this; }
public void remove()
{ throw new UnsupportedOperationException(); }
public boolean hasNext()
{ return (_next != null); }
public TNode next()
{
if (_next == null)
throw new NoSuchElementException();
TNode current = _next;
_next = getFirstChild(current);
for (TNode ancestor = current;
(ancestor != null) && (_next == null);
ancestor = getParent(ancestor))
{
_next = getNextSibling(ancestor);
}
return current;
}
protected abstract TNode getFirstChild(TNode of);
protected abstract TNode getNextSibling(TNode of);
protected abstract TNode getParent(TNode of);
}
(It's mildly naughty in that it implements Iterator<E> and Iterable<E> on the same object. This just means that you have to new up a fresh object in order to iterate a second time; don't try to reuse the same object).
This means that if your hierarchical structure consists of nodes for which you can define those three simple navigational operations, then all you have to do is derive your own equivalent of SimpleNodeIterator. This makes it very easy to enable this capability on any tree implementation.
If what you're iterating doesn't have a way to get the parent, you need to keep a stack during the iteration. Each time you descend a level, you push the state for the current level onto the stack. When you finish iterating at the current level, you pop the last state off the stack and continue with it. When the stack is empty, you're done. This means you have some intermediate storage, but its maximum size is proportional to the depth of the recursion rather than the number of items, so assuming the data is roughly balanced then it should be a lot more storage-efficient than copying all the items to a list before you return it.
The final solution I found after some refactoring is to implement variant b) but to pass a Visitor instead of the result collection:
private void traverse(final Element element, final Visitor... visitors) {
for (final Visitor visitor : visitors)
// push e.g. the parent path to the stack
visitor.push(visitor.visit(element));
for (final Element child: getElementChildren(element))
traverse(child, visitors);
for (final Visitor visitor : visitors)
visitor.pop();
}
The Visitor provides also a stack to carry the information about the parent path. This solution allows me to separate the traversal logic from the collection logic, without the need of the more complex TreeIterator implementation.
private class CollectPathsVisitor extends ElementVisitor {
public final Set<String> paths = new TreeSet<String>();
public Object visit(Element element) {
final IPath parentPath = (IPath) peek();
final IPath path = parentPath.append(element.getLabel());
if (!hasChildren(element))
paths.add(path);
return path;
}
}
I usually prefer to return the result, since i think
$result = extractPaths($arg,$arg2);
is more clear than
extractPaths($arg,$arg2,$result);
but it's entirely based on taste.
I would choose option b, since it would create fewer objects and thereby be more efficient. Solution a feels more like the way you would do it in a functional language, but that relies on assumptions that don't hold in Java.
If you pass in the object to be built, if you had an exception that you caught in a place where you had a reference to that object, then you would at least have the data you built up until the exception was thrown.
I personally pass in Builders as arguments when multiple methods will be "building" on it, including recursion. This way you only have a single object being built, and miss out lots of Set, Map or List copying.
in this specific case I prefer the latter solution since:
it avoids creating throw-away collections
your algorithm implemented in this way cannot get any gain from being "functional"
imho there is no real benefit of being functional without a really good reason f (e.g. using threads).
pass a collection as parameter for this method
Later will create less objects in memory (as already said) but also manages each tree path only once: when extracted and stored in the Set result it is not 'addedAll' to any other set again and again and again.