In an attempt to create a N-ary tree with multiple node with different type of node objects[Country | State etc], I tried modifying the below generic class from -
https://github.com/vivin/GenericTree/blob/master/src/main/java/net/vivin/GenericTreeNode.java
I tried the following -
package com.mycompany.ds;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class GenericTreeNode<T>{
private T data;
private List<GenericTreeNode<? super T>> children;
private GenericTreeNode<? super T> parent;
public GenericTreeNode() {
super();
children = new ArrayList<GenericTreeNode<? super T>>();
}
public GenericTreeNode(T data) {
this();
setData(data);
}
public GenericTreeNode<? super T> getParent() {
return this.parent;
}
public List<GenericTreeNode<? super T>> getChildren() {
return this.children;
}
public int getNumberOfChildren() {
return getChildren().size();
}
public boolean hasChildren() {
return (getNumberOfChildren() > 0);
}
public void setChildren(List<GenericTreeNode<? super T>> children) {
for(GenericTreeNode<? super T> child : children) {
child.parent = this;
}
this.children = children;
}
public void addChild(GenericTreeNode<? super T> child) {
child.parent = this;
children.add(child);
}
public void addChildAt(int index, GenericTreeNode<T> child) throws IndexOutOfBoundsException {
child.parent = this;
children.add(index, child);
}
public void removeChildren() {
this.children = new ArrayList<GenericTreeNode<? super T>>();
}
public void removeChildAt(int index) throws IndexOutOfBoundsException {
children.remove(index);
}
public GenericTreeNode<? super T> getChildAt(int index) throws IndexOutOfBoundsException {
return children.get(index);
}
public T getData() {
return this.data;
}
public void setData(T data) {
this.data = data;
}
public String toString() {
return getData().toString();
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
GenericTreeNode<?> other = (GenericTreeNode<?>) obj;
if (data == null) {
if (other.data != null) {
return false;
}
} else if (!data.equals(other.data)) {
return false;
}
return true;
}
/* (non-Javadoc)
* #see java.lang.Object#hashCode()
*/
#Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((data == null) ? 0 : data.hashCode());
return result;
}
public String toStringVerbose() {
String stringRepresentation = getData().toString() + ":[";
for (GenericTreeNode<? super T> node : getChildren()) {
stringRepresentation += node.getData().toString() + ", ";
}
//Pattern.DOTALL causes ^ and $ to match. Otherwise it won't. It's retarded.
Pattern pattern = Pattern.compile(", $", Pattern.DOTALL);
Matcher matcher = pattern.matcher(stringRepresentation);
stringRepresentation = matcher.replaceFirst("");
stringRepresentation += "]";
return stringRepresentation;
}
}
But errors in the following methods -
public void setChildren(List<GenericTreeNode<? super T>> children) {
for(GenericTreeNode<? super T> child : children) {
child.parent = this;
}
this.children = children;
}
public void addChild(GenericTreeNode<? super T> child) {
child.parent = this;
children.add(child);
}
Errors -
1 - Type mismatch: cannot convert from GenericTreeNode<T> to GenericTreeNode<? super capture#2-of ? super
T>
2 - Type mismatch: cannot convert from GenericTreeNode<T> to GenericTreeNode<? super capture#4-of ? super
T>
How can I fix these?
You could create a class / interface that represents a GISEntity and create the generic tree node whose generic type T extends GISEntity. This would allow you to have nodes of different kinds of GISEntity subclasses-- Country / State etc.
To build up on the answer of ditkin:
after having made all your classes implement or extend GISEntity, you would write your tree this way:
public class GenericTreeNode<T extends GISEntity>{
private T data;
private List<GenericTreeNode<? extends GISEntity>> children;
private GenericTreeNode<? extends GISEntity> parent;
public GenericTreeNode() {
super();
children = new ArrayList<GenericTreeNode<? extends GISEntity>>();
}
////////
......
////////
public void addChild(GenericTreeNode<? extends GISEntity> child) {
child.parent = this;
children.add(child);
}
public void addChildAt(int index, GenericTreeNode<? extends GISEntity> child) throws IndexOutOfBoundsException {
child.parent = this;
children.add(index, child);
}
////////
......
////////
}
Note that it will not really help you to avoid class casting. The thing is that as soon as you have added children to your node, when you retrieve them you just know that they are GISEntity, because of type erasure. So this technique only give you a bit of type safety.
It's not a good idea to use Generic in order to store different types of objects in the same collection. What you should do is to create an hierarchy and use it to store your objects. With a good design, the base class will have all that's necessary to access the different objects without casting; otherwise you will have to write some cast here and there. Here is an example of code (please note that the design here is far from beeing optimal and is simply to show the use of virtual function and polymorphism) :
static class GISEntity {
final String name;
public GISEntity (String name) { this.name = name; }
public String getName() { return name; }
public String getTypeName() { return "GISEntity"; }
public String toString() { return name; }
}
//
static class Country extends GISEntity {
final String typeName = "country";
public Country (String name) { super(name); }
public String getTypeName() { return typeName; }
public String toString() { return name; }
}
//
static class State extends GISEntity {
public State (String name) { super(name); }
public String getTypeName() { return "state"; }
public String toString() { return name; }
}
//
static class Territory extends GISEntity {
public Territory (String name) { super(name); }
public String getTypeName() { return "territory"; }
public String toString() { return name; }
}
//
// Here's an example of subclassing GenericTreeNode<GISEntity>:
//
static class IsATerritory extends GenericTreeNode<GISEntity> {
IsATerritory (String name) { super (new Territory (name)); }
public GISEntity getData() {
State s = new State (super.getData().getName().toUpperCase());
return s; }
};
//
// Here we put some data. Note that the order of insertion is important
// for the tree and that it's not alphabetical in this example.
//
GenericTree<GISEntity> earth = new GenericTree<GISEntity>() ;
//
GenericTreeNode<GISEntity> ListOfCountries = new GenericTreeNode<GISEntity>(new GISEntity("List of countries"));
//
GenericTreeNode<GISEntity> US = new GenericTreeNode<GISEntity>(new Country("United States"));
GenericTreeNode<GISEntity> Washington = new GenericTreeNode<GISEntity>(new State("Washington"));
GenericTreeNode<GISEntity> Florida = new GenericTreeNode<GISEntity>(new State("Florida"));
//
GenericTreeNode<GISEntity> Canada = new GenericTreeNode<GISEntity>(new Country("Canada"));
//
// We are now using some different ways for creating the nodes:
//
#SuppressWarnings("unchecked")
List<GenericTreeNode<GISEntity>> CanadaProvinces = new ArrayList<GenericTreeNode<GISEntity>>(
Arrays.asList(new GenericTreeNode<GISEntity>(new State("Quebec")),
new GenericTreeNode<GISEntity>(new State("Ontario")))
);
//
US.addChild(Washington);
US.addChild(Florida);
//
// Here's are two examples of subclassing; this time with anonymous classes.
// Don't forget that these two anonymous classes will hold an hidden reference
// to the outer classe as they are not static!
//
GenericTreeNode<GISEntity> alberta = new GenericTreeNode<GISEntity>() {
{ setData(new State ("Alberta")); }
public GISEntity getData() {
State s = new State (super.getData().getName().toUpperCase());
return s;
}
};
//
GenericTreeNode<GISEntity> saskatchewan = new GenericTreeNode<GISEntity>(new State ("saskatchewan")) {
public GISEntity getData() {
State s = new State (super.getData().getName().toUpperCase());
return s; }
};
//
CanadaProvinces.add(alberta);
CanadaProvinces.add(saskatchewan);
//
// Other ways for creating the nodes:
CanadaProvinces.add(new GenericTreeNode<GISEntity>(new State("Manitoba")));
//
// Note the use of the IsATerritory subclass:
CanadaProvinces.add(new IsATerritory("Northwest Territories"));
//
Canada.setChildren(CanadaProvinces);
//
ListOfCountries.addChild(Canada);
ListOfCountries.addChild(US);
//
earth.setRoot(ListOfCountries);
//
System.out.println(earth.toString());
System.out.println();
System.out.println(earth.toStringWithDepth());
System.out.println();
System.out.println(ListOfCountries.toStringVerbose());
//
List<GenericTreeNode<GISEntity>> loc = earth.build(GenericTreeTraversalOrderEnum.PRE_ORDER);
System.out.println(loc);
//
Map<GenericTreeNode<GISEntity>, Integer> locd = earth.buildWithDepth(GenericTreeTraversalOrderEnum.PRE_ORDER);
System.out.println(locd);
//
Map<GenericTreeNode<GISEntity>, Integer> locd2 = earth.buildWithDepth(GenericTreeTraversalOrderEnum.POST_ORDER);
System.out.println(locd2);
//
// Two examples of iteration; showing both the use of the instanceof operator
// and of virtual (or override) functions:
//
for (GenericTreeNode<GISEntity> gen: loc) {
GISEntity data = gen.getData();
if (data instanceof State) {
System.out.println("Is State: " + data.getName());
} else if (data instanceof Country) {
System.out.println("Is Country: " + data.getName());
} else {
System.out.println(data.getTypeName() + data.getName());
}
}
//
for (Entry<GenericTreeNode<GISEntity>, Integer> entry: locd.entrySet()) {
GISEntity data = entry.getKey().getData();
Integer depth = entry.getValue();
if (data instanceof State) {
System.out.println(depth.toString() + ": Is State: " + data.getName());
} else if (data instanceof Country) {
System.out.println(depth.toString() + ": Is Country: " + data.getName());
} else {
System.out.println(depth.toString() + ": " + data.getTypeName() + data.getName());
}
}
In this example, I have subclassed the class GenericTreeNode in three different ways (two anonymous classes, one a named class) in order to change the getData so that it will return a new GISEntity where the name has been replaced with its UpperCase copy.
Note that will all these three subclasses, I'm using GenericTreeNode<GISEntity> and not something like GenericTreeNode<Territory>. This is because that even if Territory is a subclass of GISEntry, the class GenericTreeNode<Territory> is not a subclass of GenericTreeNode<GISEntry>.
For using something like a mix of GenericTreeNode<Territory> with GenericTreeNode<GISEntry>, we have to use the ? extends GISEntry and ? super GISEntry and this will multiply by one thousand the complexity of the generic code. Unless that you want to make some heavy subclassing of the generic classes GenericTree<> and GenericTreeNode<>, it's totally useless to use the ? type; even for a collecting different types of objects. Unless that you have years of experience in generic code, don't use the ? notation. Most projects will do totally fine with the simpler generic code.
I've also added some examples of iterations over the generic tree for both the build() and the buildWithDepth() functions for those interested.
Finally, as a reference, this generic tree is explained in http://vivin.net/2010/01/30/generic-n-ary-tree-in-java/ (3 pages).
Related
I have a simple class that wraps an unmodifiable list (it is used as a context for a query):
public class Context<T extends Node> implements Iterable<T> {
private final List<T> m_nodes;
Context(List<T> nodes) {
m_nodes = Collections.unmodifiableList(nodes);
}
#Override
public Iterator<T> iterator() {
return m_nodes.iterator();
}
public int indexOf(T node) {
return m_nodes.indexOf(node);
}
public boolean isEmpty() {
return m_nodes.isEmpty();
}
// context are always sorted by pre value
public boolean containsAll(FDMContext<T> other) {
Iterator<T> oit = other.iterator();
Iterator<T> sit = iterator();
int c = 0;
while( oit.hasNext()) {
T o = oit.next();
while( sit.hasNext()) {
T s = sit.next();
if( s.getPre() == o.getPre()) {
c++;
break;
}
}
}
return c == other.size();
}
public int size() {
return m_nodes.size();
}
public List<T> getNodes() {
return m_nodes;
}
#Override
public String toString() {
return m_nodes.toString();
}
}
The Node class has a Value subclass. I don't think the details of the Node and Value class are important, except that they share a common getPre() method defining a unique node identifier.
If there is a method that expects an Context<Value> parameter but I have an Context<Node> instance as a result of a query where I am certain it only contains Value instances, is it safe to suppress warnings?
e.g.
static Entry makeEntry(Event event, Group head, Group tail, Context<Value> values) {
/* code */
}
...
Context<Node> ctx = query(anotherCtx,somePath); // the result ctx contains only Value objects. The query method returns a Context(Node) object
#SuppressWarnings({ "rawtypes", "unchecked" })
Entry ret = makeEntry( this, headGrp, tailGrp, (Context)tail);
I could create a new context of type Context<Value>, but I'd rather avoid the copy if possible.
I have a class that do a tree from directory, subdirectory and files. And need to satisfy two conditions. Sort the contents in following way:
-Directories should go first.
-Directories and files are sorted in lexicographic order (case-insensitive).
I work on windows system and code below work fine, all is sorted how I want. Directories go first and are sorted in lexicographic order. But I read that on windows system is automatically sorted in lexicographic order. But this code doesn't work on Linux, the line children.sort(Comparator.comparing(file -> file.isDirectory() ? -1 : 1)); do not work and files\directory aren't sorted in lexicographic order (case-insensitive). How to solve problem with sort conditions on linux system? How can change sort conditions in code?
import static java.util.Comparator.comparing;
public class TreeNode<T> implements Iterable<TreeNode<T>> {
public T data;
public TreeNode<T> parent;
public List<TreeNode<T>> children;
public boolean isRoot() {
return parent == null;
}
private List<TreeNode<T>> elementsIndex;
public TreeNode(T data) {
this.data = data;
this.children = new LinkedList<TreeNode<T>>();
this.elementsIndex = new LinkedList<TreeNode<T>>();
this.elementsIndex.add(this);
}
public TreeNode<T> addChild(T child) {
TreeNode<T> childNode = new TreeNode<T>(child);
childNode.parent = this;
this.children.add(childNode);
this.registerChildForSearch(childNode);
return childNode;
}
private void registerChildForSearch(TreeNode<T> node) {
elementsIndex.add(node);
if (parent != null)
parent.registerChildForSearch(node);
}
#Override
public String toString() {
return data != null ? data.toString() : "[data null]";
}
#Override
public Iterator<TreeNode<T>> iterator() {
TreeNode<T> iter = new TreeNode<T>((T) this);
return (Iterator<TreeNode<T>>) iter;
}
public static TreeNode<File> createDirTree(File folder) {
if (!folder.isDirectory()) {
throw new IllegalArgumentException("folder is not a Directory");
}
List<File> children = Arrays.asList(folder.listFiles());
children.sort(Comparator.comparing(file -> file.isDirectory() ? -1 : 1));
TreeNode<File> DirRoot = new TreeNode<File>(folder);
for (File file : children) {
if (file.isDirectory()) {
appendDirTree(file, DirRoot);
} else {
appendFile(file, DirRoot);
}
}
return DirRoot;
}
public static void appendDirTree(File folder, TreeNode<File> dirRoot) {
dirRoot.addChild(folder);
List<File> children = Arrays.asList(folder.listFiles());
children.sort(comparing(file -> file.isDirectory() ? -1 : 1));
for (File file : children) {
if (file.isDirectory()) {
appendDirTree(file, dirRoot.children.get(dirRoot.children.size() - 1));
} else {
appendFile(file, dirRoot.children.get(dirRoot.children.size() - 1));
}
}
}
public static void appendFile(File file, TreeNode<File> filenode) {
filenode.addChild(file);
}
public static String renderDirectoryTree(TreeNode<File> tree) {
List<StringBuilder> lines = renderDirectoryTreeLines(tree);
String newline = "\n";
StringBuilder sb = new StringBuilder(lines.size() * 20);
for (StringBuilder line : lines) {
sb.append(line);
sb.append(newline);
}
//System.out.println(sb);
return sb.toString();
}
public static List<StringBuilder> renderDirectoryTreeLines(TreeNode<File> tree) {
List<StringBuilder> result = new LinkedList<>();
result.add(new StringBuilder().append(tree.data.getName() + " " + calculateFileSize(tree) + " bytes"));
Iterator<TreeNode<File>> iterator = tree.children.iterator();
while (iterator.hasNext()) {
List<StringBuilder> subtree = renderDirectoryTreeLines(iterator.next());
if (iterator.hasNext()) {
addSubtree(result, subtree);
} else {
addLastSubtree(result, subtree);
}
}
return result;
}
private static void addSubtree(List<StringBuilder> result, List<StringBuilder> subtree) {
Iterator<StringBuilder> iterator = subtree.iterator();
result.add(iterator.next().insert(0, "├─ "));
while (iterator.hasNext()) {
result.add(iterator.next().insert(0, "│ "));
}
}
private static void addLastSubtree(List<StringBuilder> result, List<StringBuilder> subtree) {
Iterator<StringBuilder> iterator = subtree.iterator();
result.add(iterator.next().insert(0, "└─ "));
while (iterator.hasNext()) {
result.add(iterator.next().insert(0, " "));
}
}
public static long calculateFileSize(TreeNode<File> tree) {
long fileSize = 0;
if (tree.data.isDirectory()) {
List<TreeNode<File>> children = tree.children;
for (TreeNode<File> child : children) {
fileSize += calculateFileSize(child);
}
} else {
fileSize = tree.data.length();
}
return fileSize;
}
}
A short version could look something like this.
Comparator<File> lexicographicFileComparator = Comparator.comparing(File::isFile)
.thenComparing(Comparator.naturalOrder());
You've wrote that File.compareTo doesn't even check whether the file exists or not. I don't think it's the job of compareTo or Comparator to check if a file exists. Files that don't exist should be filtered before.
I think your example is overusing static methods. TreeNode should be an abstract class, while you have an implementation called FileTreeNode that replaces your static methods.
Here is an example
TreeNode
public abstract class TreeNode<N extends TreeNode<N, T>, T> implements Iterable<N> {
private static <N extends TreeNode<N, ?>> Stream<CharSequence> renderBranch(N node) {
Stream.Builder<CharSequence> result = Stream.builder();
result.add(node.printNode());
Iterator<N> iterator = node.getChildren().iterator();
while (iterator.hasNext()) {
Stream<CharSequence> subtree = renderBranch(iterator.next());
Iterator<CharSequence> subtreeIterator = subtree.iterator();
String branchSplit = "├─ ";
String branchSpacer = "│ ";
if (!iterator.hasNext()) {
branchSplit = "└─ ";
branchSpacer = " ";
}
result.add(branchSplit + subtreeIterator.next());
while (subtreeIterator.hasNext()) {
result.add(branchSpacer + subtreeIterator.next());
}
}
return result.build();
}
private final T data;
private final N parent;
private final List<N> children;
public TreeNode(T data) {
this(data, null);
}
protected TreeNode(T data, N parent) {
this.data = data;
this.parent = parent;
children = initChildren();
}
/**
* Called in constructor to initialize the children list.
*/
protected abstract List<N> initChildren();
/**
* Used to avoid unsafe casting.
*
* #return This
*/
protected abstract N instance();
/**
* TreeNode knows how to print the tree, but not how to print the current element. This way the child class can decide how it wants to be printed in the tree;
*
* #return readable text representation of node.
* #see TreeNode#renderBranch()
*/
protected abstract CharSequence printNode();
/**
* #return Returns a string representation of the entire branch starting at this node.
*/
public String renderBranch() {
Stream<CharSequence> lines = renderBranch(instance());
return lines.collect(Collectors.joining("\n"));
}
/**
* #return Returns a stream of the entire branch starting at this node
*/
public Stream<N> streamBranch() {
return Stream.concat(Stream.of(instance()), getChildren().stream().flatMap(TreeNode::streamBranch));
}
public T getData() {
return data;
}
public N getParent() {
return parent;
}
public List<N> getChildren() {
return children;
}
public boolean isRoot() {
return parent == null;
}
#Override
public String toString() {
return data != null ? data.toString() : "[data null]";
}
#Override
public Iterator<N> iterator() {
// No clue what you want to do here, but your method will not work.
return children.iterator();
}
}
FileTreeNode
public class FileTreeNode extends TreeNode<FileTreeNode, File> {
public FileTreeNode(File root) {
super(root);
}
protected FileTreeNode(File data, FileTreeNode parent) {
super(data, parent);
}
public long calculateFileSize() {
return streamBranch().mapToLong(value -> value.getData().length()).sum();
}
#Override
protected CharSequence printNode() {
return getData().getName() + " " + calculateFileSize() + " bytes";
}
#Override
protected List<FileTreeNode> initChildren() {
File file = getData();
if (file.isDirectory()) {
File[] files = file.listFiles();
if (files != null) {
return Arrays.stream(files)
.sorted(Comparator.comparing(File::isFile).thenComparing(Comparator.naturalOrder()))
.map(path -> new FileTreeNode(path, this))
.toList();
}
}
return Collections.emptyList();
}
#Override
protected FileTreeNode instance() {
return this;
}
}
Usage
File file = new File("C:\\dir");
FileTreeNode rootNode = new FileTreeNode(file);
System.out.println(rootNode.renderBranch());
This way you can easily implement a TreeNode structure for other classes too. For example File is outdated and replaced by Path so maybe you will want a PathTreeNode in the future. Who knows.
I have implemented the Composite Design Pattern and then expanded the Composite class to also implement Iterable, however the iterator() method (which returns an iterator object) is also part of the abstract Component class and is then implemented by the Composite class (but not the Leaf class).
I want to implement a depth first and breadth first search for a tree-like structure. See summarized code below:
public abstract class Component {
public void add() {
}
public void remove() {
}
public ArrayList<Component> getItems() {
}
public ItemIterator iterator() {
}
public class Composite extends Component implements Iterable<Component> {
ArrayList<Component> items = new ArrayList<Component>();
String name;
public ItemIterator iterator() {
return new ItemIterator(this);
}
public Composite(String name) {
this.name = name;
}
public getName() {
// returns name
}
public ArrayList<Component> getItems() {
return this.items;
}
public class ItemIterator implements Iterator<Component> {
ArrayList<Component> breadthFirstSearch = new ArrayList<Component>();
Component currentItem;
public ItemIterator(Component firstItem) {
currentItem = firstItem;
breadthFirstSearch.add(currentItem);
}
public boolean hasNext() {
if (breadthFirstSearch.isEmpty()) {
return false;
}
return true;
}
public Component next() {
// This method pops the root item the first time, creates its children,
// places at end of ArrayList,
// then returns the root. Second time the same operations are performed
// on the following item in the breadth first traversal of the tree.
if (hasNext()) {
Component nextItem = breadthFirstSearch.get(0);
if (nextItem instanceof Composite) {
for (Component item : currentItem.getItems()) {
breadthFirstSearch.add(item);
}
}
breadthFirstSearch.remove(0);
if (hasNext()) {
currentItem = breadthFirstSearch.get(0);
}
return nextItem;
}
return null;
}
public class Demo {
public static void main(String[] args) {
Component bag = new Composite("bag");
Component plasticBag = new Composite("plastic bag");
Component makeupBag = new Composite("makeup bag");
Component phone = new Composite("phone");
Component lipstick = new Composite("lipstick");
Component mascara = new Composite("mascara");
bag.add(plasticBag); bag.add(makeupBag);
plasticbag.add(phone); makeupBag.add(lipstick); makeupBag.add(mascara);
ItemIterator itr = bag.iterator();
while (itr.hasNext()) {
System.out.println(itr.next().getName());
}
}
}
The code above compiles and runs fine, it works. However, I am not certain of whether it is programmatically acceptable. The structure of it seems to fundamentally go against other Iterator implementations that I have seen (implementations that I discovered after finishing the above solution), but I can't quite grasp/explain what is so wrong about it. The other way of implementing Iterable (in a different context) was of the form:
public abstract class Component {
public void add() {
}
public void remove() {
}
public ArrayList<Component> getItems() {
}
}
Note the lack of an iterator() method in the abstract class above.
public class Composite extends Component implements Iterable<Component> {
ArrayList<Component> items = new ArrayList<Component>();
String name;
public Iterator<Component> iterator() {
return new Iterator() {
public boolean hasNext() {
// Code
}
public Iterator<Component> next() {
// Code
};
}
public Composite(String name) {
this.name = name;
}
public getName() {
// returns name
}
public ArrayList<Component> getItems() {
return this.items;
}
}
Which way of structuring the solution is better, and is my way of doing it outright wrong/bad practice and if so, why? I am new to Java, so I apologize if this turns out to be a bad question.
I think you described the visitor pattern:
interface Visitable {
void accept(Visitor v);
}
class Visitor {
void visit(Component c){
c.doFooBar();// implement your logic here
}
}
class Component implements Visitable {
private List<Component> children;
void accept(Visitor v){
v.visit(this);
children.forEach(child -> child.accept(v)); // sumbit the visitor/iterator down the composite tree
}
}
public static void main(String[] args){
Component composite = Factory.createComposite();
composite.accept(new Visitor());
}
Instead of having iterator build up a list of pending items to iterate, it should just store a list of pending iterators to traverse.
Here is a Minimal, Reproducible Example:
public final class Node {
private final String name;
private List<Node> children = new ArrayList<>();
public Node(String name) {
this.name = name;
}
public Node(String name, Node... children) {
this.name = name;
this.children.addAll(Arrays.asList(children));
}
public String getName() {
return this.name;
}
public List<Node> getChildren() {
return this.children;
}
public Iterable<Node> breadthFirstSearch() {
return () -> new NodeIterator(this, true);
}
public Iterable<Node> depthFirstSearch() {
return () -> new NodeIterator(this, false);
}
#Override
public String toString() {
return "Node[" + this.name + "]";
}
}
public final class NodeIterator implements Iterator<Node> {
private final Deque<Iterator<Node>> iterators = new ArrayDeque<>();
private final boolean breadthFirst;
public NodeIterator(Node node, boolean breadthFirst) {
this.iterators.add(Collections.singleton(node).iterator());
this.breadthFirst = breadthFirst;
}
#Override
public boolean hasNext() {
return ! this.iterators.isEmpty();
}
#Override
public Node next() {
Iterator<Node> iterator = this.iterators.removeFirst();
Node node = iterator.next();
if (iterator.hasNext())
this.iterators.addFirst(iterator);
if (! node.getChildren().isEmpty()) {
if (this.breadthFirst)
this.iterators.addLast(node.getChildren().iterator());
else
this.iterators.addFirst(node.getChildren().iterator());
}
return node;
}
}
Test
Node root = new Node("root",
new Node("1",
new Node("1.1",
new Node("1.1.1"),
new Node("1.1.2")),
new Node("1.2",
new Node("1.2.1"),
new Node("1.2.2"))
),
new Node("2",
new Node("2.1",
new Node("2.1.1"),
new Node("2.1.2")),
new Node("2.2",
new Node("2.2.1"),
new Node("2.2.2"))));
for (Node node : root.breadthFirstSearch())
System.out.println(node);
System.out.println();
for (Node node : root.depthFirstSearch())
System.out.println(node);
Output
Node[root]
Node[1]
Node[2]
Node[1.1]
Node[1.2]
Node[2.1]
Node[2.2]
Node[1.1.1]
Node[1.1.2]
Node[1.2.1]
Node[1.2.2]
Node[2.1.1]
Node[2.1.2]
Node[2.2.1]
Node[2.2.2]
Node[root]
Node[1]
Node[1.1]
Node[1.1.1]
Node[1.1.2]
Node[1.2]
Node[1.2.1]
Node[1.2.2]
Node[2]
Node[2.1]
Node[2.1.1]
Node[2.1.2]
Node[2.2]
Node[2.2.1]
Node[2.2.2]
just a quick question. I have a jar file and I want to integrate it within my project. The decompiled version of the jar file looks like this:
public abstract class SinglyLinkedList
protected SinglyLinkedList.Item head;
public void addFront(String s) {
if (head == null) {
head = new SinglyLinkedList.Item(s);
}
else {
SinglyLinkedList.Item insert = new SinglyLinkedList.Item(s);
next = head;
head = insert;
}
}
public void add(String[] array) { String[] arrayOfString;
int j = (arrayOfString = array).length; for (int i = 0; i < j; i++)
{ String s = arrayOfString[i];
addFront(s);
}
}
public String toString()
{
if (head == null) {
return "empty list";
}
String s = "";
for (SinglyLinkedList.Item helper = head; next != null; helper = next) {
s = String.valueOf(s) + value + ", ";
s = String.valueOf(s) + value;
return s;
}
public abstract void sort();
protected class Item
{
public Item next;
public String value;
public Item(String value) {
this.value = value;
}
}
}
Now I want to create a new linked list in my other class, but this doesnt work:
private SinglyLinkedList linkedlist;
public SelectionSortableList(){
this.linkedlist = new SinglyLinkedList();
}
He cannot instantiate it. Can please somebody tell me why he don't want to instantiate it? Thank you in beforehand.
public abstract class SinglyLinkedList
Because this is an abstract class. abstract classes cannot be instantiated.
You need to extend it and implement the following abstract method:
public abstract void sort();
Once you extend the class all the non-private method will be directly accessible into your super class.
Your class should be like this:
class SelectionSortableList extends SinglyLinkedList {
#Override
public void sort() {
// provide you implementation here.
}
}
#Entity
#NamedQueries({
#NamedQuery(
name = "FolderNode.findByName",
query = "SELECT f FROM FolderNode f WHERE f.name = :name AND f.parentNode = :parentNode"),
#NamedQuery(
name = "FolderNode.findRootNodeByName",
query = "SELECT f FROM FolderNode f WHERE f.name = :name AND f.parentNode is null")
})
public class FolderNode extends InstructorTreeNode {
public FolderNode() {
super();
}
public FolderNode(String name) {
this();
setName(name);
}
public FolderNode(int sortOrder, String name) {
this(name);
this.sortOrder = sortOrder;
}
public FolderNode(int sortOrder, String name, EmployeeState status) {
this(sortOrder, name);
this.status = status;
}
public static FolderNode addWaitingListNode(String name) {
EntityManager em = getDao().getEntityManager();
em.getTransaction().begin();
FolderNode waitingListNode = getWaitingListFolder();
FolderNode folderNode = new FolderNode(0, name);
waitingListNode.addChild(folderNode);
em.merge(waitingListNode);
em.getTransaction().commit();
em.close();
return folderNode;
}
public static void addWaitingListStudent(String waitingList, Student s) {
EntityManager em = FolderNode.getDao().getEntityManager();
em.getTransaction().begin();
FolderNode waitingListsNode = getWaitingListFolder();
FolderNode waitingListNode = getDao().findFolderNodeByName(waitingListsNode, waitingList);
waitingListNode.addChild(new EmployeeLeaf(s.getInmate()));
em.merge(waitingListNode);
em.getTransaction().commit();
em.close();
}
public static FolderNode getAMClassFolder() {
return getDao().findFolderNodeByName(getStudentsFolder(), "AM Class");
}
public static FolderNode getAttendanceFolder() {
return getDao().findFolderNodeByName(getRootFolder(), "Employee Attendance");
}
public static FolderNode getFormerParaprosFolder() {
return getDao().findFolderNodeByName(getParaprosFolder(), "Former");
}
public static FolderNode getFormerStudentsFolder() {
return getDao().findFolderNodeByName(getStudentsFolder(), "Former");
}
public static FolderNode getPMClassFolder() {
return getDao().findFolderNodeByName(getStudentsFolder(), "PM Class");
}
public static FolderNode getParaprosFolder() {
return getDao().findFolderNodeByName(getRootFolder(), "Parapros");
}
public static FolderNode getPendingStudentsFolder() {
return getDao().findFolderNodeByName(getRootFolder(), "Pending Students");
}
public static FolderNode getRootFolder() {
return getDao().findFolderNodeByName(null, EducationPreferences.getInstructor().getInstructorName());
}
public static FolderNode getStudentsFolder() {
return getDao().findFolderNodeByName(getRootFolder(), "Students");
}
public static FolderNode getWaitingListFolder(String name) {
FolderNode waitingListsNode = getWaitingListFolder();
return getDao().findFolderNodeByName(waitingListsNode, name);
}
public static FolderNode getWaitingListFolder() {
return getDao().findFolderNodeByName(getRootFolder(), "Waiting List");
}
public static void setClassFolder(Student aStudent, EntityManager entityManager) {
EntityManager em = entityManager;
if (entityManager == null) {
em = FolderNode.getDao().getEntityManager();
em.getTransaction().begin();
}
EmployeeLeaf leaf = EmployeeLeaf.findActiveStudentLeaf(aStudent);
FolderNode node = aStudent.getShift() == Shift.AM ? getAMClassFolder() : getPMClassFolder();
leaf.setParentNode(node);
em.merge(leaf);
GlobalEntityMethods.updateHistory(leaf);
if (entityManager == null) {
em.getTransaction().commit();
em.close();
}
}
public static void transferWaitingListStudent(String currentFolder, String toFolder, Student student) {
EntityManager em = FolderNode.getDao().getEntityManager();
em.getTransaction().begin();
FolderNode waitingListsNode = getWaitingListFolder();
FolderNode currentWaitingListNode = getDao().findFolderNodeByName(waitingListsNode, currentFolder);
EmployeeLeaf employeeLeaf = EmployeeLeaf.getDao().findWaitingListLeafByInmate(student.getInmate());
currentWaitingListNode.removeChild(employeeLeaf);
FolderNode toWaitingListNode = getDao().findFolderNodeByName(waitingListsNode, toFolder);
toWaitingListNode.addChild(employeeLeaf);
em.merge(currentWaitingListNode);
em.merge(toWaitingListNode);
em.getTransaction().commit();
em.close();
}
public void addChild(InstructorTreeNode node) {
childNodes.add(node);
node.setParentNode(this);
}
public List<InstructorTreeNode> getChildNodes() {
Collections.sort(childNodes);
return childNodes;
}
#Override
public Set<Inmate> getInmates() {
Set<Inmate> inmateSet = new HashSet<> (50);
for (InstructorTreeNode node: getChildNodes()) {
inmateSet.addAll(node.getInmates());
}
return inmateSet;
}
public int getSortOrder() {
return sortOrder;
}
public EmployeeState getStatus() {
return status;
}
#Override
public List<InstructorTreeNode> getTree() {
List <InstructorTreeNode> result = new ArrayList<> (25);
for (InstructorTreeNode childNode: getChildNodes()) {
if (childNode instanceof FolderNode) {
result.add(childNode);
}
result.addAll(childNode.getTree());
}
return result;
}
#Override
public JPanel getView(EmployeeViewController controller) {
if ("Employee Attendance".equals(getName())) {
return new AttendanceView();
} else if ("Waiting List".equals(getName())) {
return new AllWaitingListsPanel(controller);
} else if (getParentNode().getName().equals("Waiting List")) {
return new WaitingListPanel(controller);
} else if ("Pending Students".equals(getName())) {
return new PendingStudentsPanel(controller);
} else if ("Students".equals(getName())) {
return new AllStudentsPanel(controller);
} else if ("AM Class".equals(getName())) {
return new AllStudentsPanel(controller, Shift.AM);
} else if ("PM Class".equals(getName())) {
return new AllStudentsPanel(controller, Shift.PM);
} else if (getParentNode().getName().equals("Students") && "Former".equals(getName())) {
return new FormerStudentsPanel(controller);
} else if ("Parapros".equals(getName())) {
return new AllParaprosPanel(controller);
} else if (getParentNode().getName().equals("Parapros") && "Former".equals(getName())) {
return new FormerParaprosPanel(controller);
}
throw new UnsupportedOperationException("unknown folder");
}
public void removeChild(InstructorTreeNode node) {
childNodes.remove(node);
node.setParentNode(null);
}
public void removeEmployeeLeaf(Inmate inmate) {
for (InstructorTreeNode node: childNodes) {
if (node instanceof EmployeeLeaf) {
EmployeeLeaf employeeLeaf = (EmployeeLeaf) node;
if (employeeLeaf.getInmate().equals(inmate)) {
childNodes.remove(employeeLeaf);
break;
}
}
}
}
public void setChildNodes(List<InstructorTreeNode> childNodes) {
this.childNodes = childNodes;
}
public void setSortOrder(int sortOrder) {
this.sortOrder = sortOrder;
}
public void setStatus(EmployeeState status) {
this.status = status;
}
#OneToMany(mappedBy = "parentNode", cascade = CascadeType.ALL, orphanRemoval = true)
private List<InstructorTreeNode> childNodes;
private int sortOrder;
#Enumerated(EnumType.STRING)
private EmployeeState status;
}
#Entity
#Table(catalog = "education", name = "instructortreenode", uniqueConstraints = #UniqueConstraint(columnNames = {
"PARENTNODE_ID", "NAME"
}))
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public abstract class InstructorTreeNode implements Comparable<InstructorTreeNode> {
public InstructorTreeNode() {
super();
}
public static InstructorTreeNodeDAO getDao() {
return dao;
}
#Override
public int compareTo(InstructorTreeNode o) {
if (o instanceof FolderNode && this instanceof FolderNode) {
FolderNode thisFolder = (FolderNode) this;
FolderNode otherFolder = (FolderNode) o;
if (thisFolder.getSortOrder() != otherFolder.getSortOrder()) {
return thisFolder.getSortOrder() - otherFolder.getSortOrder();
} else {
return thisFolder.getName().compareToIgnoreCase(otherFolder.getName());
}
} else if (o instanceof EmployeeLeaf && this instanceof EmployeeLeaf) {
return getName().compareToIgnoreCase(((InstructorTreeNode) o).getName());
}
return (o instanceof FolderNode) ? -1 : +1;
}
public int getCount() {
return getTree().size();
}
public abstract Set<Inmate> getInmates();
public String getName() {
return name;
}
public FolderNode getParentNode() {
return parentNode;
}
public abstract List<InstructorTreeNode> getTree();
public abstract JPanel getView(EmployeeViewController theController);
public void setName(String name) {
this.name = name;
}
public void setParentNode(FolderNode parentNode) {
this.parentNode = parentNode;
}
#Override
public String toString() {
return name;
}
private static final InstructorTreeNodeDAO dao = new InstructorTreeNodeDAO();
private String name;
#ManyToOne
private FolderNode parentNode;
}
Here is my problem:
The Collections.sort line works just fine in Java 8u5 and before, but
in Java 8u20 they seem to have changed the code for Collections.sort
and it no longer uses anything but the natural order, even if you specify
a Comparator.
Should I be using another method to sort my list, or is there an error in
Collections.sort.
Any help would be much appreciated, as this is driving me crazy.
I forgot to say that this code does not use a specified comparator, but according to the documentation it is supposed to use the CompareTo, if your class implements Comparable, which is what I am using.
I tried also specifying a comparator, but it did not work either.
Since Collections.sort now delegates to List.sort, the actual List implementation has an impact. Implementations like ArrayList and Vector take the opportunity to implement List.sort in a more efficient manner than the default implementation as they pass their internal array directly to Arrays.sort omitting the copy steps of the default implementation.
This works seamlessly unless programmers use the anti-pattern of subclassing an implementation (rather than using delegation) overriding methods to implement a contradicting behavior. Lazily populated lists like these from EclipseLink/JPA are known to have problems with this as they try to intercept every reading method to populate the list before proceeding but miss the new sort method. If the list hasn’t populated yet when sort is called, sort will see an empty list state.
In your code, there is no indication where the list does come from and which actual implementation class it has, but since I see a lot of familiar looking annotations, I guess, you are using such a framework…
If you use the method Collections#sort(List<T> list), it defers to the method List#sort(Comparator comparator) with comparator given as null. The source code from java.util.Collections is as follows:
public static <T extends Comparable<? super T>> void sort(List<T> list) {
list.sort(null);
}
If you want to specify your own Comparator, you need to use the method Collections#sort(List<T> list, Comparator<T> comparator), which passes on your comparator to the list sorting method. The source code from java.util.Collections is as follows:
public static <T> void sort(List<T> list, Comparator<? super T> c) {
list.sort(c);
}
So far so good. Now, as you have correctly pointed out, if you do not specify a comparator, the natural ordering of the class, that is, the compareTo method you have defined, is used.
However, the Comparable class documentation also states the following:
It is strongly recommended (though not required) that natural orderings be consistent with equals. This is so because sorted sets (and sorted maps) without explicit comparators behave "strangely" when they are used with elements (or keys) whose natural ordering is inconsistent with equals. In particular, such a sorted set (or sorted map) violates the general contract for set (or map), which is defined in terms of the equals method.
Since the class InstructorTreeNode does not override Object#equals, your compareTo method may return 0 even if == returns false. I reckon this is leading to what the documentation calls "strangely".
You might not like this answer because it doesn't give you a quick-fix for your situation, but it will help you more in the long run.
This is the kind of bug that you can figure out yourself with a little debugging. I don't know what IDE you are using, but with Eclipse, you can even step into code that is in the JDK!
So, what I would do, is set a breakpoint at the line where you call sort() on the childNodes. Then I would step into the JDK code and just walk through it myself. It will become very clear what is going on and why it isn't calling your compare function.
You could try to build a custom comparator. Here is an example how that should look. This is for comparing BigDecimals.
class YourComparator implements Comparator<InstructorTreeNode> {
#Override
public int compare(final InstructorTreeNode 01, final InstructorTreeNode o2) {
return o2.getYourCompVal().compareTo(o1.getYourCompVal());
}
}
public List<InstructorTreeNode> getChildNodes() {
Collections.sort(childNodes, new YourComparator());
return childNodes;}