I have a generic tree class in which each tree node holds some data. Each piece of data has one attribute of the type String. I want to sort each tree node's children alphabetically by this attribute.
The Tree class:
public class Tree<T>{
public T data;
public List<Tree<T>> children = new ArrayList<Tree<T>>();
}
Note that the tree's children are of type Tree!
An example actual type parameter for the Tree class is the following:
public class DataItem{
public String name;
}
My idea is to extend the Tree class with a sort() method and use a Comparator like the following but I am stuck at the comparison function:
public class Tree<T>{
public T data;
public List<Tree<T>> children = new ArrayList<Tree<T>>();
public void sort(){
Collections.sort(this.children,
new Comparator<Tree<T>>(){
#Override
public int compare(Tree<T> objectA, Tree<T> objectB){
//I am stuck here!
return 0;
}
}
);
for(Tree<T> child: this.children){
child.sort();
}
}
}
I have different ideas to solve this problem:
Use reflection to acces the objects' attributes and compare them.
Implement the interface Comparable in DataItem.
Use a new interface to acces the objects' attribute for comparison:
public interface GetComparisonAttribute {
public String getComparisonAttribute();
}
public class DataItem implements GetComparisonAttribute{
public String name;
#Override
public String GetComparisonAttribute(){
return this.name;
}
}
//the comparison function inside Tree<T>.sort():
public int compare(Tree<T> objectA, Tree<T> objectB){
return objectA.data.getComparisonAttribute()
.compareToIgnoreCase(objectB.data.getComparisonAttribute());
}
What is the right or best thing to do? Are there any other ways?
It may be important to be able to specify the sorting attribute.
I think it would be nice to use Collections.sort() directly on a Tree but implementing it in this recursive data structure really confuses me. A downside of doing it this way is that I cannot specify the sorting attribute.
Try this:
public class Tree<T> {
public T data;
public List<Tree<T>> children = new ArrayList<Tree<T>>();
private Class<T> type;
public Tree(Class<T> t) {
type = t;
}
public void sort(){
Collections.sort(this.children,
new Comparator<Tree<T>>(){
#Override
public int compare(Tree<T> objectA, Tree<T> objectB){
if (type==DataItem.class)
{
DataItem diA = (DataItem) (objectA.data);
DataItem diB = (DataItem) (objectB.data);
return diA.name.compareTo(diB.name);
}
else
throw new IllegalArgumentException();
}
}
);
for(Tree<T> child: this.children){
child.sort();
}
}
}
You should pass the type T of the class Tree when you create it. Then you can downcast to DataItem and sort the list according to the filed you like. You can check of course also against other type parameters aside from DataItem.
public void sort(final Comparator<? super T> dataComparator)
{
Collections.sort(this.children,
new Comparator<Tree<T>>()
{
#Override
public int compare(Tree<T> treeA, Tree<T> treeB)
{
return dataComparator.compare(treeA.getData(), treeB.getData());
}
}
);
for(Tree<T> child: this.children)
{
child.sort(dataComparator);
}
}
void test()
{
Tree<DataItem> tree = new Tree<>();
tree.sort(new Comparator<DataItem>()
{
#Override
public int compare(DataItem dataA, DataItem dataB)
{
return dataA.getName().compareTo(dataB.getName());
}
});
}
In java8, this can be simplified as
public void sort(final Comparator<? super T> dataComparator)
{
Collections.sort(this.children,
Comparator.comparing(Tree::getData, dataComparator));
for(Tree<T> child: this.children)
{
child.sort(dataComparator);
}
}
void test()
{
Tree<DataItem> tree = new Tree<>();
tree.sort( Comparator.comparing(DataItem::getName) );
}
Related
One of the advantages of streams is that you can avoid visiting the whole structure for some operations, like anyMatch or filter+findFirst.
However, if you have your own data structure, depending on how you turn it into a stream you may end up visiting it all anyway.
What is the right way to turn a custom tree data type into a stream?
Consider the following example:
interface Tree{
void forEach(Consumer<Integer> c);
}
final class EmptyTree implements Tree{
public void forEach(Consumer<Integer> c){}
}
interface NonEmptyTree extends Tree{}
record Leave(int label) implements NonEmptyTree{
public void forEach(Consumer<Integer> c){
System.out.println("In forEachLeave "+label);
c.accept(label);
}
}
record Node(NonEmptyTree left, NonEmptyTree right) implements NonEmptyTree{
public void forEach(Consumer<Integer> c){
left.forEach(c); right.forEach(c);
}
}
The two main ways to turn a tree into a stream would be
var sb=Stream.<Integer>builder();
myTree.forEach(sb);
sb.build()
or
Stream.of(myTree).mapMulti(Tree::forEach)
However, both of them call forEach, thus both of them will visit all the tree (and call the prints for all the labels, in this example).
How do you implement a .stream() method in the Tree type so that it would not even visit the whole tree if it is not needed? (because of .anyMatch, for example)
Ok, I sorted it.
I'm quite sure that what I'm doing is pretty standard with immutable trees
(parent fields only make sense in mutable trees)
Here is my result, for reference for future programmers doing streams on immutable trees.
The class TreeIterator<E> is the one really relevant to this ordeal.
I could make nested classes to be able to make more stuff private, but as a code example I think it is more clear in this non nested form.
interface Tree<E> extends Iterable<E>{
Tree<E> and(Tree<E> other);
default Tree<E> left(){ return empty(); }
default Tree<E> right(){ return empty(); }
default E label(Supplier<E> orElse){ return orElse.get(); }
#SuppressWarnings("unchecked")
static <E> Tree<E> empty(){ return (Tree<E>)EmptyTree.empty; }
static <E> Tree<E> leaf(E label){ return new Leaf<E>(label); }
default Stream<E> stream(){ return StreamSupport.stream(spliterator(), false); }
}
final class EmptyTree<E> implements Tree<E>{
public Tree<E> and(Tree<E> other){ return other; }
private EmptyTree(){} //Singleton pattern: only one EmptyTree can exists
static final Tree<?> empty = new EmptyTree<>();
public Iterator<E> iterator(){ return List.<E>of().iterator(); }
public String toString(){ return "<EMPTY>"; }
}
interface NonEmptyTree<E> extends Tree<E>{
Leaf<E> itAdvance(ArrayList<NonEmptyTree<E>> stack);
default Tree<E> and(Tree<E> other){
if (!(other instanceof NonEmptyTree<E> net)){ return this; }
return new Node<E>(this, net);
}
}
record Leaf<E>(E label) implements NonEmptyTree<E>{
public E label(Supplier<E> orElse){ return label; }
public Leaf<E> itAdvance(ArrayList<NonEmptyTree<E>> stack){ return this; }
public Iterator<E> iterator(){ return List.<E>of(label).iterator(); }
public String toString(){ return label+""; }
}
record Node<E>(NonEmptyTree<E> left, NonEmptyTree<E> right) implements NonEmptyTree<E>{
public Node{ assert left!=null && right!=null; }//null is not a valid tree
public Leaf<E> itAdvance(ArrayList<NonEmptyTree<E>> stack){
stack.add(right);
return left.itAdvance(stack);
}
public Iterator<E> iterator(){ return new TreeIterator<E>(this); }
public String toString(){ return "("+left+", "+right+")"; }
}
class TreeIterator<E> implements Iterator<E>{
private final ArrayList<NonEmptyTree<E>> stack = new ArrayList<>(32);
public boolean hasNext(){ return !stack.isEmpty(); }
public TreeIterator(Node<E> n){ stack.add(n); }
public E next(){
if(stack.isEmpty()){ throw new NoSuchElementException(); }
var last=stack.remove(stack.size()-1);
return last.itAdvance(stack).label();
}
}
Looking at record definition Leave(int label) implements NonEmptyTree, I have two questions:
Did you mean "leaf"?
A tree consists of nodes (either a leaf or an internal node), but a node or leaf do not implement a tree, i.e., they are not are specific type of a tree. Are you sure about your node/leaf/tree implementation?
I would recommend a simple implementation like this one here: https://www.baeldung.com/java-binary-tree
When it comes to stream, you have two options:
Implement your own Stream-enabled class (see discussion here)
Provide a method that returns a (specific) stream, e.g. filtered.
Keep in mind that there are many different trees out there, e.g. red–black tree, n-ary tree, AVL Tree, B-Tree ...
I am extending a class and also extending it's inner class but I'm getting errors trying to get the inner class through the outer child class.
public class SMONArray extends SMONArr {
public class Element extends SMONArr.Element {
protected int x, y, w, h;
protected Element(String string, Object arr) {
super(string, arr);
}
}
public Element getSelected(String key, int index) {
return get(key, index);
}
}
public class SMONArr {
protected final LinkedHashMap<String, ArrayList<Element>> arr = new LinkedHashMap<>();
public class Element {
public String key;
public Object elem;
protected Element(String string, Object arr) {
key = string;
elem = arr;
}
}
public Element get(String key, int index) {
return arr.get(key).get(index);
}
}
Like it is above I'm getting an error in the SMONArray child class getSelected function which is:
Type mismatch: cannot convert from SMONArr.Element to SMONArray.Element
Intellij suggests to cast the returned value to Element like this:
public Element getSelected(String key, int index) {
return (Element) get(key, index);
But like this the error goes away and I can run the program, but when I call the getSelected function I get a different error:
Exception in thread "main" java.lang.ClassCastException: SMONArr$Element cannot be cast to SMONArray$Element
If I don't extend the inner class I have no problems but if I do, how can I then get the inner class through the outer child class?
EDIT: The array arr in SMONArr is populated with ArrayList of Element of which Element is a String key and an int[] e.g. key, [1, 2, 3, 4] is what Element is.
intArr[j] = Integer.parseInt(strArr[j]);
linesArr.add(new Element(parts[0].trim(), intArr));
arr.put(key, linesArr);
Add a createElement class to your parent class:
public class SMONArr {
// ...
// Alternatively, this can be abstract (assuming you make the class abstract too)
// then it has to be overridden in subclasses.
public Element createElement(String key, Object value) {
return new Element(key, value);
}
// ...
}
then you can override this in the subclass:
public class SMONArray extends SMONArr {
// ...
// Now Element refers to the subclass.
public Element createElement(String key, Object value) {
return new Element(key, value);
}
// ...
}
and invoke createElement instead of new Element in the parent class. Then the cast in the subclass will work without a ClassCastException.
You can do this without the need for the getSelected method in subclasses, by making the SMONArr class generic:
public abstract class SMONArr<E extends Element> {
protected final LinkedHashMap<String, ArrayList<E>> arr = new LinkedHashMap<>();
public class Element { ... }
// ...
// Now this has to be abstract.
public abstract E createElement(String key, Object value);
// ...
}
public class SMONArray extends SMONArr<SMONArray.Element> {
public class Element extends SMONArr.Element { ... }
public Element createElement(String key, Object value) {
return new Element(key, value);
}
}
You have two element types. You have a collection in the SMONArr class that can only hold one of the types.
The call to 'get' in SMONArr class returns a SMONArr.Element, which is not a SMONArray.Element.
I don't know the context - perhaps in this case the collection is guaranteed to hold only SMONArray.Elements, in which case a cast would do the trick.
Your issue has nothing to do with extending the inner class, you return the wrong type and don't see it because of the naming confusion.
This is what you're doing:
public class Outer1 {
class Inner1 {}
public Inner1 get() {
return new Inner1();
}
}
public class Outer2 extends Outer1 {
public class Inner2 extends Outer1.Inner1 {}
public Inner2 getSelected() {
return get();
}
}
You getSelected() is declared as returning the subtype, but then you call the method returning the supertype.
You can change its return type to Inner1, or in your case SMONArr.Element.
public SMONArr.Element getSelected(String key, int index) {
return get(key, index);
}
I have the following java code:
public class TreeNode<T> {
public TreeNode<T> getParent() {
return null;
}
public void setParent(TreeNode<T> parent) {
}
public List<TreeNode<T>> getChildren() {
return null;
}
public void setChildren(List<TreeNode<T>> children) {
}
public T getData() {
return null;
}
public void setData(T data) {
}
}
Now, I want to create a class that extends the one above, like the following:
public class BinaryTreeNode<T> extends TreeNode<T> {
public BinaryTreeNode<T> getLeftChild() {
return null;
}
public void setLeftChild() {
}
public BinaryTreeNode<T> getRightChild() {
return null;
}
public void setRightChild() {
}
#Override
public void setChildren(List<BinaryTreeNode<T>> children) {
}
}
However, the last method won't compile because the parameter children is not of type List<TreeNode<T>>. I understand why this happens (because List<BinaryTreeNode<T>> is not considered a subtype of List<TreeNode<T>>), but what is the best way to fix something like this?
I know that I can just define the children parameter to be of type List<TreeNode<T>>, but if possible I would like to enforce it to be of type List<BinaryTreeNode<T>>.
The issue is not coming from the lists not being subclasses, it's because you can't override a method and upcast its parameter.
if you have the following classes:
public class A
{
public void f(A a) {}
}
public class B extends A
{
public void problem(){}
public void f(B a)
{
a.problem();
}
}
Since B extends from A, you could have the following code running:
A a1 = new B();
A a2 = new A();
a1.f(a2);
when that code will run, it will try to execute the function "problem" on an A instance that doesn't have it.
That's why in general you can't override the parameters with classes that inherit the original type.
Now, you can leave the parameter as List> children, but then you might get nodes that aren't binary, which I assume you don't want.
If you're sure that's the way you want the inheritance to be, you can check the type of "children" at the beginning of the method and throw an exception if it doesn't fit.
To sum up what shmosel proposed:
public class TreeNode<T, N extends TreeNode<T, N>> {
public N getParent() { return null; }
public void setParent(N parent) {}
public List<N> getChildren() { return null; }
public void setChildren(List<N> children) {}
public T getData() { return null; }
public void setData(T data) {}
}
The downside is that your Nodes now have to by typed with a "redundant" generic type. That is because you try to counter the fact that sub classes can act as object of super classes.
Your Binary Node class would look like this:
class BinaryTreeNode<T> extends TreeNode<T, BinaryTreeNode<T>>
As an alternative and for further reading, try this article:
https://www.sitepoint.com/self-types-with-javas-generics/
I have a List defined with generics. All elements in this list define the interface TransferableData. Depending on the object that eventually populates the list, i want to call a method to do something with the data.
So I want to archive something like this:
private <T extends TransferableData> String doSomething(List<T> data, Class<T> dataType){
if(returnType.equals(A.class)){
for(T singleElement : data){
((A)singleElement).methodInsideA();
}
}else if(returnType.equals(B.class)){
for(T singleElement : data){
((B)singleElement).methodInsideB();
}
}
}
I realize that this is NOT the way to do this. I just cannot find a proper way without all the if-else and the unchecked casting.
Based on the assumption that your classes are implementing TransferableData interface, you can add a method invokeMethod() which has void as return type. For below solution to work, you should have same method signature in all the classes.
public interface TransferableData {
void invokeMethod();
}
public class A implements TransferableData {
#Override
public void invokeMethod() {
//Your business logic A
}
}
public class B implements TransferableData {
#Override
public void invokeMethod() {
//Your business logic for B
}
}
Iterate over your list of objects as (No need to pass dataType to this method):
private <T extends TransferableData> String doSomething(List<T> data) {
for(T object: data){
object.invokeMethod();
}
//return the string according to your business logic.
}
As per the comment from Mena you can try something like this,
public interface TransferableData {
void genericInvoker();
}
public class A implements TransferableData {
#Override
public void genericInvoker() {
methodInsideA();
}
void methodInsideA(){
// Do specific thing to A
}
}
public class B implements TransferableData {
#Override
public void genericInvoker() {
methodInsideB();
}
void methodInsideB(){
// Do specific thing to B
}
}
Then you can invoke the methods as follows:
private <T extends TransferableData> String doSomething(List<T> data, Class<T> dataType){
for(T singleElement : data){
singleElement.genericInvoker();
}
//Don't forget to return a String from this method
}
Consider this code:
public class TreeNode<T extends TreeNode<T, E>, E> {
protected T parent;
protected E data;
protected List<T> children = new ArrayList<T>();
public TreeNode(T parent, E data) {
this.parent = parent;
this.data = data;
}
public T getRoot() {
if (parent == null) {
return this; //<---- Problem is here!
} else {
return getParent().getRoot();
}
}
public T getParent() {
if (parent == null) {
throw new RuntimeException("This already the parent!");
} else {
return parent;
}
}
}
/*
incompatible types
required: T
found: TreeNode<T,E>
*/
How can I fix that and make my code work?
You want to use the so-called getThis() trick. Declare a new method like so:
/** Subclasses must implement this method as {#code return this;} */
protected abstract T getThis();
Then when you need to use this, just call getThis() instead. As a side note, implementing this method will confound a class like BadNode in #Michael Williamson's answer, thus making it harder to write such a class in the first place (which is a good thing).
It's not guaranteed that the type T is the same as the type of the class itself, so you need to add a cast to the line that doesn't compile:
public T getRoot() {
if (parent == null) {
return (T)this;
} else {
return getParent();
}
}
To give a simple example of code that will expose the typing error:
public class GoodNode extends TreeNode<GoodNode, Integer> {
public GoodNode(GoodNode parent, Integer data) {
super(parent, data);
}
}
public class BadNode extends TreeNode<GoodNode, Integer> {
public BadNode(GoodNode parent, Integer data) {
super(parent, data);
}
public static void main(String[] args) {
GoodNode node = new BadNode(null, null).getRoot();
}
}
Running BadNode.main causes a typing error since BadNode(null, null).getRoot() returns an object of class BadNode (since it has no parent), but because BadNode extends TreeNode<GoodNode, Integer>, the return type of getRoot() is GoodNode. Since BadNode cannot be cast to GoodNode, there's a class cast exception:
Exception in thread "main" java.lang.ClassCastException: BadNode cannot be cast to GoodNode
at BadNode.main(BadNode.java:7)
Maybe you are trying to be "too generic"?
public class TreeNode<E> {
protected TreeNode<E> parent;
protected E data;
protected List<TreeNode<E>> children = new ArrayList<TreeNode<E>>();
public TreeNode(T parent, E data) {
this.parent = parent;
this.data = data;
}
public TreeNode<E> getRoot() {
if (parent == null) {
return this;
} else {
return getParent(); // <--- ???
}
}
...
}
BTW: You might want to call something along the lines of parent.getRoot() instead of getParent().
Why do you use a raw type in the extends clause? That might hinder type inference.
Try the following:
public class TreeNode<T extends TreeNode<T,E>, E> {