I would like to implement a trampoline in java by returning a thunk whenever I hit a StackOverflowError. Are there any guarantees about the StackOverflowError, like, if the only thing I do after the StackOverflowError is creating objects on the heap and returning from functions, I will be fine?
If the above sounds vague, I have added some code for computing even/odd in a tail-recursive manner in continuation passing style, returning a delayed thunk whenever the stack flows over. The code works on my machine, but does Java guarantee that it will always work?
public class CPS {
public static class Thunk {
final Object r;
final Continuation c;
final boolean isDelayed;
public Object force() {
Thunk t = this;
while (t.isDelayed)
t = t.compute();
return t.r;
}
public Thunk compute() {
return this;
}
public Thunk(Object answer) {
isDelayed = false;
r = answer;
c = null;
}
public Thunk(Object intermediate, Continuation cont) {
r = intermediate;
c = cont;
isDelayed = true;
}
}
public static class Continuation {
public Thunk apply(Object result) {
return new Thunk(result);
}
}
public static Thunk even(final int n, final Continuation c) {
try {
if (n == 0) return c.apply(true);
else return odd(n-1, c);
} catch (StackOverflowError x) {
return new Thunk(n, c) {
public Thunk compute() {
return even(((Integer)n).intValue(), c);
}
};
}
}
public static Thunk odd(final int n, final Continuation c) {
try {
if (n == 0) return c.apply(false);
else return even(n-1, c);
} catch (StackOverflowError x) {
return new Thunk(n, c) {
public Thunk compute() {
return odd(((Integer)n).intValue(), c);
}
};
}
}
public static void main(String args[]) {
System.out.println(even(100001, new Continuation()).force());
}
}
I tried the following implementation possibilities:
A) With thunks (see code CPS below)
B) Without thunks as suggested by chris (see code CPS2 below)
C) With thunks with the stack overflow replaced by a depth check (see code CPS3 below)
In each case I checked if 100,000,000 is an even number. This check lasted
A) about 2 seconds
B) about 17 seconds
C) about 0.2 seconds
So returning from a long chain of functions is match faster than throwing an exception that unwinds that chain. Also, instead of waiting for a stack overflow, it is much faster to just record the recursion depth and unwind at depth 1000.
Code for CPS:
public class CPS {
public static class Thunk {
final Object r;
final boolean isDelayed;
public Object force() {
Thunk t = this;
while (t.isDelayed)
t = t.compute();
return t.r;
}
public Thunk compute() {
return this;
}
public Thunk(Object answer) {
isDelayed = false;
r = answer;
}
public Thunk() {
isDelayed = true;
r = null;
}
}
public static class Continuation {
public Thunk apply(Object result) {
return new Thunk(result);
}
}
public static Thunk even(final int n, final Continuation c) {
try {
if (n == 0) return c.apply(true);
else return odd(n-1, c);
} catch (StackOverflowError x) {
return new Thunk() {
public Thunk compute() {
return even(n, c);
}
};
}
}
public static Thunk odd(final int n, final Continuation c) {
try {
if (n == 0) return c.apply(false);
else return even(n-1, c);
} catch (StackOverflowError x) {
return new Thunk() {
public Thunk compute() {
return odd(n, c);
}
};
}
}
public static void main(String args[]) {
long time1 = System.currentTimeMillis();
Object b = even(100000000, new Continuation()).force();
long time2 = System.currentTimeMillis();
System.out.println("time = "+(time2-time1)+", result = "+b);
}
}
Code for CPS2:
public class CPS2 {
public abstract static class Unwind extends RuntimeException {
public abstract Object compute();
public Object force() {
Unwind w = this;
do {
try {
return w.compute();
} catch (Unwind unwind) {
w = unwind;
}
} while (true);
}
}
public static class Continuation {
public Object apply(Object result) {
return result;
}
}
public static Object even(final int n, final Continuation c) {
try {
if (n == 0) return c.apply(true);
else return odd(n-1, c);
} catch (StackOverflowError x) {
throw new Unwind() {
public Object compute() {
return even(n, c);
}
};
}
}
public static Object odd(final int n, final Continuation c) {
try {
if (n == 0) return c.apply(false);
else return even(n-1, c);
} catch (StackOverflowError x) {
return new Unwind() {
public Object compute() {
return odd(n, c);
}
};
}
}
public static void main(String args[]) {
long time1 = System.currentTimeMillis();
Unwind w = new Unwind() {
public Object compute() {
return even(100000000, new Continuation());
}
};
Object b = w.force();
long time2 = System.currentTimeMillis();
System.out.println("time = "+(time2-time1)+", result = "+b);
}
}
Code for CPS3:
public class CPS3 {
public static class Thunk {
final Object r;
final boolean isDelayed;
public Object force() {
Thunk t = this;
while (t.isDelayed)
t = t.compute();
return t.r;
}
public Thunk compute() {
return this;
}
public Thunk(Object answer) {
isDelayed = false;
r = answer;
}
public Thunk() {
isDelayed = true;
r = null;
}
}
public static class Continuation {
public Thunk apply(Object result) {
return new Thunk(result);
}
}
public static Thunk even(final int n, final Continuation c, final int depth) {
if (depth >= 1000) {
return new Thunk() {
public Thunk compute() {
return even(n, c, 0);
}
};
}
if (n == 0) return c.apply(true);
else return odd(n-1, c, depth+1);
}
public static Thunk odd(final int n, final Continuation c, final int depth) {
if (depth >= 1000) {
return new Thunk() {
public Thunk compute() {
return odd(n, c, 0);
}
};
}
if (n == 0) return c.apply(false);
else return even(n-1, c, depth+1);
}
public static void main(String args[]) {
long time1 = System.currentTimeMillis();
Object b = even(100000000, new Continuation(), 0).force();
long time2 = System.currentTimeMillis();
System.out.println("time = "+(time2-time1)+", result = "+b);
}
}
That's an interesting way to jump up the stack. It seems to work, but is probably slower than the usual way to implement this technique, which is to throw an exception that is caught $BIGNUM layers up the call stack.
Related
class Color implements Comparable<Color>{
private long RValue;
private long GValue;
private long BValue;
public long mix;
public Color() {
this.RValue=0;
this.GValue=0;
this.BValue=0;
this.mix=0;
}
public Color(long c) {
this.RValue=c;
this.GValue=c;
this.BValue=c;
this.mix=c;
}
private void calcMix() {
this.mix=256*256*this.RValue+256*this.GValue+1*this.BValue;
}
public long getRValue() {
return this.RValue;
}
public long getGValue() {
return this.GValue;
}
public long getBValue() {
return this.BValue;
}
public void setRValue(long RValue) {
this.RValue=RValue;
calcMix();
}
public void setGValue(long GValue) {
this.GValue=GValue;
calcMix();
}
public void setBValue(long BValue) {
this.BValue=BValue;
calcMix();
}
public String toString() {
return this.RValue+" "+this.GValue+" "+this.BValue+" "+this.mix;
}
public boolean equals(Object r) {
if(r==this)
return true;
if(r==null)
return false;
if(r.getClass()!=this.getClass())
return false;
Color color=(Color) r;
if(this.RValue!=color.RValue || this.GValue!=color.GValue || this.BValue!=color.BValue)
return false;
return true;
}
public int compareTo(Color c) {
if(this.mix<c.mix)
return -1;
else if(this.mix==c.mix)
return 0;
else
return 1;
}
}
class ColorRectangle extends Color implements Comparable<Color>{
private int iX1,iY1,iX2,iY2;
public ColorRectangle() {
super();
iX1=0;
iY1=0;
iX2=0;
iY2=0;
}
public ColorRectangle(int iX1,int iY1,int iX2,int iY2,long color) {
super(color);
this.iX1=iX1;
this.iY1=iY1;
this.iX2=iX2;
this.iY2=iY2;
}
public int getIX1() {
return iX1;
}
public int getIY1() {
return iY1;
}
public int getIX2() {
return iX2;
}
public int getIY2() {
return iY2;
}
public void setIX1(int iX1) {
this.iX1=iX1;
}
public void setIY1(int iY1) {
this.iY1=iY1;
}
public void setIX2(int iX2) {
this.iX2=iX2;
}
public void setIY2(int iY2) {
this.iY2=iY2;
}
public int calcArea() {
return ((Math.max(this.iX1, this.iX2)-Math.min(this.iX1, this.iX2))*
(Math.max(this.iY1, this.iY2)-Math.min(this.iY1, this.iY2)));
}
public int calcPerimeter() {
return (2*(Math.max(this.iX1, this.iX2)-Math.min(this.iX1, this.iX2))+
2*(Math.max(this.iY1, this.iY2)-Math.min(this.iY1, this.iY2)));
}
public int compareTo(ColorRectangle r) {
if(this.calcArea()<r.calcArea())
return -1;
else if(this.calcArea()==r.calcArea())
return 0;
else
return 1;
}
public String toString() {
return iX1+" "+iY1+" "+iX2+" "+iY2+" "+mix;
}
public boolean equals(ColorRectangle r) {
if(!(r instanceof ColorRectangle))
return false;
ColorRectangle rect=r;
return (this.calcArea()==rect.calcArea() && this.getRValue()==rect.getRValue()
&& this.getGValue()==rect.getGValue() && this.getBValue()==rect.getBValue());
}
public void translateX(int iPoints) {
this.iX1+=iPoints;
this.iX2+=iPoints;
}
public void translateY(int iPoints) {
this.iY1+=iPoints;
this.iY2+=iPoints;
}
public void translateXY(int iPoints) {
this.translateX(iPoints);
this.translateY(iPoints);
}
public boolean isInside(int ptX,int ptY) {
if(ptX>iX1 && ptX<iX2 && ptY>iY1 && ptY<iY2)
return true;
return false;
}
public ColorRectangle unionRect(ColorRectangle r) {
int x1=Math.min(Math.min(this.iX1, this.iX2), Math.min(r.getIX1(),r.getIX2()));
int x2=Math.max(Math.min(this.iX1, this.iX2),Math.max(r.getIX1(), r.getIX2()));
int y1=Math.min(Math.min(this.iY1, this.iY2), Math.min(r.getIY1(),r.getIY2()));
int y2=Math.max(Math.min(this.iY1, this.iY2),Math.max(r.getIY1(), r.getIY2()));
long color=256*256*this.getRValue()+256*this.getGValue()+1*this.getBValue();
return new ColorRectangle(x1,x2,y1,y2,color);
}
public ColorRectangle intersectionRect(ColorRectangle r) {
int x1=Math.min(this.iX1,r.iX2);
int x2=Math.max(this.iX1,r.iX2);
int y1=Math.min(this.iY1, r.iY2);
int y2=Math.max(this.iY1, r.iY2);
long color=256*256*this.getRValue()+256*this.getGValue()+1*this.getBValue();
return new ColorRectangle(x1,x2,y1,y2,color);
}
}
class RectangleCollection{
private SortedSet recSet;
public RectangleCollection(String fileName) throws IOException{
try {
RandomAccessFile file=new RandomAccessFile(fileName,"r");
String line="";
String[] info=new String[5];
recSet=new TreeSet<ColorRectangle>();
while((line=file.readLine()) != null && line.length()>0 ) {
info=line.split(" ");
recSet.add(new ColorRectangle(Integer.parseInt(info[0]),
Integer.parseInt(info[1]),Integer.parseInt(info[2]),Integer.parseInt(info[3]),
Long.parseLong(info[4])));
}
file.close();
}
catch(FileNotFoundException e) {
System.out.println(e.getMessage());
}
}
public void print() {
Iterator<RectangleCollection>iterator=recSet.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next()+" ");
}
}
}
This is my main function:
public static void main(String[] args)throws IOException {
RectangleCollection recCol=new RectangleCollection("rects.txt");
recCol.print();
}
So i have this code in java and the class RectangleCollection.
My task is to create interface SortedSet of type TreeSet which will be for storing ColorRectangle objects. ColorRectangle class has 5 data members: x1,x2,y1,y2,color.
I have to make explicit constructor with name -> filename from which i will read ColorRectangle objects which i will add in TreeSet.
and this is my texfile rects.txt:
-10 -10 6 10 255
-1 -1 10 6 255
-2 -2 10 6 255
*-3 -1 10 6 255
-1 -1 10 6 255
Qhen i debug it, the print function shows me only the first line from the textfile. Can you tell me how to fix it?
I am developing a client-server in java and I encountered problem passing a custom Runnable implementation as argument from an object to another.
The problem is that the Runnable code is evaluated (not executed) at definition but I want it to be evaluated at invocation.
Is there any way to achieve this behavior?
Here the code affected by this problem:
Custom Runnable implementation
public abstract class ChallengeReportDelegation implements Runnable
{
private ChallengeReport fromChallengeReport = null;
private ChallengeReport toChallengeReport = null;
#Override
public abstract void run();
public ChallengeReport getFromChallengeReport()
{
return fromChallengeReport;
}
public ChallengeReport getToChallengeReport()
{
return toChallengeReport;
}
public void setFromChallengeReport(ChallengeReport fromChallengeReport)
{
this.fromChallengeReport = fromChallengeReport;
}
public void setToChallengeReport(ChallengeReport toChallengeReport)
{
this.toChallengeReport = toChallengeReport;
}
}
Here where the Runnable is passed as argument:
// Record challenge
this.challengesManager.recordChallenge(whoSentRequest, whoConfirmedRequest,
new ChallengeReportDelegation()
{
#Override
public void run()
{
ChallengeReport fromReport = getFromChallengeReport();
ChallengeReport toReport = getToChallengeReport();
sendMessage(whoSentRequest, new Message(MessageType.CHALLENGE_REPORT, String.valueOf(fromReport.winStatus), String.valueOf(fromReport.challengeProgress), String.valueOf(fromReport.scoreGain)));
sendMessage(whoConfirmedRequest, new Message(MessageType.CHALLENGE_REPORT, String.valueOf(toReport.winStatus), String.valueOf(toReport.challengeProgress), String.valueOf(toReport.scoreGain)));
}
});
The receiving object store the ChallengeReportDelegation instance as completionOperation, wait for a timeout then execute this code.
private void complete()
{
stopTranslations();
int fromStatus;
int toStatus;
if (this.fromScore > this.toScore)
{
fromStatus = 1;
toStatus = -1;
}
else if (this.fromScore < this.toScore)
{
fromStatus = -1;
toStatus = 1;
}
else
{
fromStatus = 0;
toStatus = 0;
}
this.completionOperation.setFromChallengeReport(new ChallengeReport(this.from, fromStatus,this.fromTranslationsProgress, this.fromScore));
this.completionOperation.setToChallengeReport(new ChallengeReport(this.to, toStatus, this.toTranslationsProgress, this.toScore));
this.completionOperation.run();
}
The code above raises a NullPointerException at the execution of the last portion of code, in the run method.
[EDIT]
The NullPointerException exception is thrown because both getFromChallengeReport() and getToChallengeReport() (second portion of code) initially return null (when the Runnable is defined and passed as argument),
but they would return consistent values at invocation time run() (third portion of code)
[EDIT2]
I reproduced the situation in this simple code:
public class TestEvaluation
{
public static void main(String[] args) throws InterruptedException
{
Middle middle = new Middle();
middle.register(new Task() {
#Override
public void run() {
System.out.println("a is: " + getA());
System.out.println("b is: " + getB());
}
});
Thread.sleep(2000);
}
abstract static class Task implements Runnable
{
private int a = 0;
private int b = 0;
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public int getB() {
return b;
}
public void setB(int b) {
this.b = b;
}
#Override
abstract public void run();
}
static class Middle
{
private ScheduledThreadPoolExecutor pool = new ScheduledThreadPoolExecutor(1);
public void register(Task task)
{
Leaf leaf = new Leaf(new Task() {
#Override
public void run() {
System.out.println("Middle");
task.run();
}
});
pool.schedule(leaf, 1, TimeUnit.SECONDS);
}
}
static class Leaf implements Runnable
{
public Task task;
public Leaf(Task task)
{
this.task = task;
}
#Override
public void run()
{
task.setA(5);
task.setB(5);
System.out.println("Leaf");
task.run();
}
}
}
The behavior that i want to achieve is the printing of
Leaf
Middle
a is: 5
b is: 5
But this is what i get
Leaf
Middle
a is: 0
b is: 0
A very simple example. Lets create a runnable with a field.
public static void main (String[] args) {
var x = new Runnable(){
int a = 0;
int getA(){
return a;
}
void setA(int v){
a = v;
}
public void run(){
System.out.println("A : " + getA());
}
};
x.run();
x.setA(5);
x.run();
}
The first time it is 0, the second time 5, because getA is evaluated when run is called.
I found a working solution for this problem, perhaps trivial for those coming from functional programming.
Accordingly to the example in last edit ([EDIT2])
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
public class TestEvaluation
{
public static void main(String[] args) throws InterruptedException
{
Middle middle = new Middle();
middle.register(new Consumer<Values>() {
#Override
public void accept(Values values) {
System.out.println("a is: " + values.getA());
System.out.println("b is: " + values.getB());
}
});
Thread.sleep(2000);
}
static class Values
{
private int a = 0;
private int b = 0;
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public int getB() {
return b;
}
public void setB(int b) {
this.b = b;
}
}
static class Middle
{
private ScheduledThreadPoolExecutor pool = new ScheduledThreadPoolExecutor(1);
public void register(Consumer<Values> passed)
{
Consumer<Values> middleConsumer = new Consumer<Values>() {
#Override
public void accept(Values values) {
System.out.println("Middle");
passed.accept(values);
}
};
Leaf leaf = new Leaf(middleConsumer);
pool.schedule(leaf, 1, TimeUnit.SECONDS);
}
}
static class Leaf implements Runnable
{
public Consumer<Values> task;
public Leaf(Consumer<Values> task)
{
this.task = task;
}
#Override
public void run()
{
Values values = new Values();
values.setA(5);
values.setB(5);
System.out.println("Leaf");
task.accept(values);
}
}
}
This code produces the behavior i want.
Hope this will help someone.
Cheers!
If you want to immediately evaluate something. I'd suggest not using a Runnable at all. It sound like an anti-pattern, trying to pass code around when all you want is the value/invocation.
Furthermore, try to use a Callable or Supplier instead since you are clearly interested in returning some values from the sub-routines.
I'm doing an exercise, in which I have to create the method add, however due to p and v being defined as objects, I'm having a hard time figuring out how I can define this method in the syntax I've been given in the exercise (I'm only allowed to change the methods).
I would like to add the two inputs 5 and 17 so that it returns 22. I've done a lot of research into other questions where I've seen them write it as Positiv(p + v) but this doesn't quite work.
public class Positiv {
public static void main(String[] args) {
try {
Positiv p = new Positiv(5);
Positiv v = new Positiv(17);
p.add(v);
System.out.println(p.get());
} catch (Exception e) {
e.printStackTrace();
}
}
private int n;
public Positiv(int n) {
if (n < 0) { throw new IllegalArgumentException("exception");
}
this.n = n;
}
public static Positiv add(Positiv v)
{
return new Positiv(n + v);
}
public int get() {
return n;
}
}
In your add method:
public static Positiv add(Positiv v)
{
return new Positiv(n + v);
}
You return a whole new Positiv object. However (correct me if I'm wrong) it looks as if you just want to add the two n fields. You can do this by adding this.get to v.get:
public void add(Positiv v)
{
this.n += v.get();
}
Which will return 22
Tutorial for this
public class HelloWorld{
public static void main(String []args){
Numbers a = new Numbers(5);
Numbers b = new Numbers(10);
int num1 = a.getN();
int num2 = b.getN();
System.out.println(addTwoNumbers(num1, num2));
}
public static int addTwoNumbers(int a, int b) {
return a + b;
}
}
class Numbers {
private int n;
public Numbers(int n) {
this.n = n;
}
public int getN() {
return n;
}
}
public class StackSimple{
private long capacity=1000;//maximum size of array
private int idx_top;
private Object data[];
public StackSimple(int capacity)
{
idx_top=-1;
this.capacity=capacity;
data = new Object[capacity];
}
public boolean isEmpty(){
return(idx_top<0);
}
public boolean isFull(){
return(idx_top>=capacity-1);
}
public int size()
{
return idx_top+1;
}
public boolean push(Object x){
if (isFull()){
throw new IllegalArgumentException("ERROR:Stack Overflow.Full Stack");
}
else
{`enter code here`data[++idx_top]=x;
return true;
}
}
public Object pop(){
if(isEmpty())
throw new IllegalArgumentException("ERROR:Stack Underflow.Empty Stack.");
else{
return data[idx_top--];
}
}
public Object top(){
if (isEmpty())
throw new IllegalArgumentException("ERROR:Stack Underflow.Empty Stack.");
else{
return data[idx_top];
}
}
public void print()
{`
for (int i=size()-1;i>=0;i--)
System.out.println(data[i]);
}
}
public class Stack_Exercise {
public static void main(String[] args) {
StackSimple s = new StackSimple(capacity:3);//error shows here
s.push(x:"books");`enter code here`
s.push(x:"something");
s.push(x:"200");
s.print();
System.out.println("Size=" +s.size());
}
}
Why doesn't this work?
Why does it say invalid statement while creating the StackSimple object? The problem is in the main class while running it. There are errors while pushing the elements.
Error while compiling
When passing parameters to a function you just pass the values.
In your case not StackSimple(capacity:3) but just StackSimple(3)
First question, which version of Java are you using.
Second, in Java you should be passing as a variable instead of StackSimple(capacity:3). Change your main method to below, here is my recommendation:
StackSimple s = new StackSimple(3);
s.push("books");
s.push("something");
s.push("200");
s.print();
System.out.println("Size=" +s.size());
You are not at all pushing the value in the stack, your pusch function is not working as it is expected to work.
Here is the correct program.
class StackSimple {
private long capacity = 1000;// maximum size of array
private int idx_top;
private Object data[];
public StackSimple(int capacity) {
idx_top = -1;
this.capacity = capacity;
data = new Object[capacity];
}
public boolean isEmpty() {
return (idx_top < 0);
}
public boolean isFull() {
return (idx_top >= capacity - 1);
}
public int size() {
return idx_top + 1;
}
public boolean push(Object x) {
if (isFull()) {
throw new IllegalArgumentException("ERROR:Stack Overflow.Full Stack");
} else {
data[++idx_top] = x;
return true;
}
}
public Object pop() {
if (isEmpty())
throw new IllegalArgumentException("ERROR:Stack Underflow.Empty Stack.");
else {
return data[idx_top--];
}
}
public Object top() {
if (isEmpty())
throw new IllegalArgumentException("ERROR:Stack Underflow.Empty Stack.");
else {
return data[idx_top];
}
}
public void print() {
for (int i = size() - 1; i >= 0; i--)
System.out.println(data[i]);
}
}
public class test {
public static void main(String[] args) {
StackSimple s = new StackSimple(3);// error shows here
s.push("books");
s.push("something");
s.push("200");
s.print();
System.out.println("Size=" + s.size());
}
}
My binary tree looks pretty close to my class materials, but when I print to the console or check for contains(), any adds I'm doing aren't registered.
I don't have a great understanding of static and the debugger is giving me a hint about making a static reference to non-static variable overallRoot, but everything compiles without error or warning in eclipse.
public class BSTSimpleSet<E extends Comparable<E>> implements SimpleSet<E> {
private GTNode<E> overallRoot;
private int size;
public static void main(String[] args) {
BSTSimpleSet<Integer> main = new BSTSimpleSet<Integer>(2);
main.toString();
main.add(3);
main.toString();
main.add(4);
main.toString();
main.add(5);
main.toString();
System.out.print(main.contains(3));
}
public BSTSimpleSet() {
size = 0;
}
public BSTSimpleSet(E input) {
overallRoot = new GTNode<E>(input);
size = 1;
}
public boolean add(E e) {
return add(e, overallRoot);
}
private boolean add(E e, GTNode<E> root) {
if (root == null) {
root = new GTNode<E>(e);
size++;
return true;
} else {
int compare = e.compareTo(root.data);
if (compare == 0) {
return false;
} else if (compare < 0) {
return add(e, root.left);
} else {
return add(e, root.right);
}
}
}
public void clear() {
overallRoot = null;
}
public boolean contains(E e) {
return contains(e, overallRoot);
}
private boolean contains(E e, GTNode<E> root) {
if (root == null) {
return false;
} else {
int compare = e.compareTo(root.data);
if (compare == 0) {
return true;
} else if (compare < 0) {
return contains(e, root.left);
} else {
return contains(e, root.right);
}
}
}
public boolean isEmpty() {
if (overallRoot == null) {
return false;
} else {
return true;
}
}
public int size() {
return size;
}
public String toString() {
this.toString(overallRoot, 0);
return null;
}
private void toString(GTNode<E> root, int level) {
if (root != null) {
for (int i = 0; i < level; i++) {
System.out.print(" ");
}
System.out.println(root.data);
toString(root.left, level + 1);
toString(root.right, level + 1);
} else {
for (int i = 0; i < level; i++) {
System.out.print(" ");
}
System.out.println("_");
}
}
private static class GTNode<E extends Comparable<E>> {
public E data;
public GTNode<E> left;
public GTNode<E> right;
public GTNode(E input) {
this(input, null, null);
}
public GTNode(E input, GTNode<E> lNode, GTNode<E> rNode) {
data = input;
left = lNode;
right = rNode;
}
}
}
This code does absolutely nothing.
private boolean add(E e, GTNode<E> root) {
if (root == null) {
root = new GTNode<E>(e);
size++;
return true;
}
...
Java passes in the Object Reference to a method. If you change the Reference, that will not
be propagated back to the calling method. If you change what the Reference refers to
that will be propagated back.
eg
// arrays behave the same way so using them to illustrate.
public void callMethods(){
int[] array = new int[1];
array[0] = 0;
doesNotChange(array);
System.out.println(array[0]);// will print 0
doesAChange(array);
System.out.println(array[0]);// will print 1
}
public void doesNotChange(int[] myArray){
myArray = new int[1];
myArray[0] = 1;
}
public void doesAChange(int[] myArray){
myArray[0] = 1;
}
To avoid these sorts of things I recommend always setting method parameters final.
The GTNode class shouldn't be static. Static classes are classes with only static methods, which means they don't have to be instantiated. The prototypical example of this is the java.lang.Math class: You don't need to call something like Math m = new Math(); m.cos(); to get the cosine, you just call Math.cos(). Since you're creating multiple instances of the GTNode class, make it non-static and you should be good.