Throwing exceptions of different classes within a sane method - java

abstract class CustomException extends Exception
{
abstract public String toString();
abstract public String getMessage();
}
interface SimpleInterestCalculator
{
public void setPrincipalAmount(int principalAmount) throws CustomException;
public int getPrincipalAmount();
public void setRateOfInterest(int rateOfInterest) throws CustomException;
public int getRateOfInterest();
public void setTime(int Time) throws CustomException;
public int getTime();
public int getSimpleInterest();
}
interface CompoundInterestCalculator
{
public void setPrincipalAmount(int principalAmount) throws CustomException;
public int getPrincipalAmount();
public void setRateOfInterest(int rateOfInterest) throws CustomException;
public int getRateOfInterest();
public void setTime(int Time) throws CustomException;
public int getTime();
public int getCompoundInterest();
}
class SimpleInterestCalculationException extends CustomException
{
String message;
SimpleInterestCalculationException(String message)
{
this.message = message;
}
SimpleInterestCalculationException()
{
this.message = null;
}
public String toString()
{
if (this.message == null)
{
return "Simple Interest Calculation Exception";
}
else
{
return "Simple Interest Calculation Exception : " + this.message;
}
}
public String getMessage()
{
return this.message;
}
}
class CompoundInterestCalculationException extends CustomException
{
String message;
CompoundInterestCalculationException(String message)
{
this.message = message;
}
CompoundInterestCalculationException()
{
this.message = null;
}
public String toString()
{
if (this.message == null)
{
return "Compound Interest Calculation Exception";
}
else
{
return "Compound Interest Calculation Exception : " + this.message;
}
}
public String getMessage()
{
return this.message;
}
}
class InterestCalculator implements SimpleInterestCalculator, CompoundInterestCalculator
{
private int principalAmount, rateOfInterest, time;
InterestCalculator()
{
this.principalAmount = 0;
this.rateOfInterest = 0;
this.time = 0;
}
InterestCalculator(int principalAmount, int rateOfInterest, int time)
{
this.principalAmount = principalAmount;
this.rateOfInterest = rateOfInterest;
this.time = time;
}
public void setPrincipalAmount(int principalAmount) throws SimpleInterestCalculationException
{
if (principalAmount < 0)
{
throw new SimpleInterestCalculationException("Principal Amount Cannot be Negative");
}
if (principalAmount == 0)
{
throw new SimpleInterestCalculationException("Principal Amount Cannot be Zero");
}
this.principalAmount = principalAmount;
}
public int getPrincipalAmount()
{
return this.principalAmount;
}
public void setRateOfInterest(int rateOfInterest) throws SimpleInterestCalculationException
{
if (rateOfInterest < 0)
{
throw new SimpleInterestCalculationException("Rate Of Interest Cannot be Negative");
}
if (rateOfInterest == 0)
{
throw new SimpleInterestCalculationException("Rate Of Interest Cannot be Zero");
}
this.rateOfInterest = rateOfInterest;
}
public int getRateOfInterest()
{
return this.rateOfInterest;
}
public void setTime(int time) throws SimpleInterestCalculationException
{
if (time < 0)
{
throw new SimpleInterestCalculationException("Time Cannot be Negative");
}
if (time == 0)
{
throw new SimpleInterestCalculationException("Time Cannot be Zero");
}
this.time = time;
}
public int getTime()
{
return this.time;
}
public int getSimpleInterest()
{
return (this.principalAmount * this.rateOfInterest * this.time) / 100;
}
public int getCompoundInterest()
{
int x, y, z;
x = (this.rateOfInterest / 100) + 1;
y = this.time;
z = 1;
while (y > 0)
{
z = z * x;
y--;
}
z = z * this.principalAmount;
return z;
}
}
class calculatepsp
{
public static void main(String gg[])
{
InterestCalculator simpleInterest = new InterestCalculator();
InterestCalculator compoundInterest = new InterestCalculator();
try
{
simpleInterest.setPrincipalAmount(-100);
simpleInterest.setRateOfInterest(5);
simpleInterest.setTime(2);
int simpleinterest = interestCalculator.getSimpleInterest();
System.out.println("Simple Interest : " + simpleinterest);
compoundInterest.setPrincipalAmount(1000);
compoundInterest.setRateOfInterest(-8);
compoundInterest.setTime(4);
int compoundinterest = interestCalculator.getCompoundInterest();
System.out.println("Compound Interest : " + compoundinterest);
}
catch (SimpleInterestCalculationException sice)
{
System.out.println(sice);
}
catch (CompoundInterestCalculationException cice)
{
System.out.println(cice);
}
}
}
I want to throw the exception of the class, if i am calculating simple interest then it should be SimpleInterestCalculationException and if i am calculating compound interest then it should be CompundInterestCalculationException.
Please Help Regarding this.

Three main options:
Make a base Exception, put it as throws on your method, make your other exceptions inherit from the base
Make multiple exceptions, use throws on your method as a comma delimted list, like public String myMethod() throws ExA, ExB, ExC
Make all your Exceptions extend Runnable, then you don't have to declare the throws (but it doesn't force your calling code to handle it either, so there is that)

Related

compareAndSet() does not work as expected

I wrote my own AtomicDouble class and I also have a BankAccount class that does two simple withdrawals and deposits operations and it has an AtomicDouble instance(balance). The problem with my code is that when I call the addAndGet method in deposit(), the program falls into an infinite loop, and compareAndSet() never returns the true value, but when I debugged this, currentValue and the value from atomic.get () were equal, but this method does not understand.
The interesting thing is that when I put if (atomic.get()==currentValue) instead of if (atomic.compareAndSet(currentValue, nextValue)), the program runs properly.
public class AtomicDouble extends Number {
private final AtomicReference<Double> atomic;
public AtomicDouble() {
this(0.0);
}
public AtomicDouble(double initialValue) {
atomic = new AtomicReference<>(initialValue);
}
public final double addAndGet(double delta) {
while (true) {
double currentValue = atomic.get();
double nextValue = currentValue + delta;
if (atomic.compareAndSet(currentValue, nextValue))
return nextValue;
}
}
public final double incrementAndGet() {
return addAndGet(1);
}
public final void set(double newValue) {
atomic.set(newValue);
}
public final double get() {
return atomic.get();
}
public final double getAndSet(double newValue) {
return atomic.getAndSet(newValue);
}
public float floatValue() {
return (float) get();
}
#Override
public double doubleValue() {
return get();
}
public int intValue() {
return (int) get();
}
public long longValue() {
return (long) get();
}
public String toString() {
return Double.toString(get());
}
}
public class BankAccount {
private final AtomicDouble balance;
private String accountNumber;
public BankAccount(double balance, String accountNumber) {
this.balance = new AtomicDouble(balance);
this.accountNumber = accountNumber;
}
public void deposit(double number, String color) {
System.out.println(color + "deposit " + number + " current balance=" + balance.addAndGet(number));
}
public void withdraw(double number, String color) {
if (this.balance.get() - number >= 0) {
System.out.println(color + "Withdraw " + number + " current balance=" + balance.addAndGet(-number));
return;
}
System.out.println(color + "Not enough balance");
}
public static void main(String[] args) {
BankAccount bankAccount = new BankAccount(1000.0, "4234236");
ExecutorService threadsPool = Executors.newFixedThreadPool(2);
threadsPool.execute(new Runnable() {
#Override
public void run() {
bankAccount.deposit(300.0, ThreadColor.ANSI_YELLOW);
bankAccount.withdraw(50.0, ThreadColor.ANSI_YELLOW);
}
});
threadsPool.execute(new Runnable() {
#Override
public void run() {
bankAccount.deposit(203.75, ThreadColor.ANSI_BLUE);
bankAccount.withdraw(100.0, ThreadColor.ANSI_BLUE);
}
});
threadsPool.shutdown();
}
}
output: There is no output
I would suppose it is because of autoboxing. You can't have a reference to double, you have a reference to Double.
The operands get "reboxed" each time around the loop and therefore references are never identical. That is, the reference in currentValue is never the same as the reference in atomic.
Try using currentValue reference types.
public final double addAndGet(double delta) {
while (true) {
Double currentValue = atomic.get();
Double nextValue = currentValue + delta;
if (atomic.compareAndSet(currentValue, nextValue))
return nextValue;
}
}
(Fortunately, Double is an immutable type, otherwise this would have a race hazard)

Why i can't create TreeSet object from file?

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?

Calling the same method multiple times without overwriting previous values

Apologies if this is trivial to most but I just can't figure this issue out!!
I am creating a mock game where I have a start, end, and hops along. There are portals where if you go on a white portal you jump further ahead and there are black ones where you go backwards. I have set up the class as a POJO;
private int totalSize;
private int minDice;
private int maxDice;
private int whitePortalStart;
private int whitePortalEnd;
private int blackPortalStart;
private int blackPortalEnd;
private int startPosition = 1;
private int currentPosition;
public GameObject(){}
public int getTotalSize() {
return totalSize;
}
public void setTotalSize(int totalSize) throws Exception {
if(totalSize <= 0){
throw new Exception("Can't have a total distance of less than or equal to 0");
} else {
this.totalSize = totalSize;
}
}
public int getMinDice() {
return minDice;
}
public void setMinDice(int minDice) throws Exception {
if(minDice <= 0){
throw new Exception("Can't have a min dice value of less than or equal to 0");
} else {
this.minDice = minDice;
}
}
public int getMaxDice() {
return maxDice;
}
public void setMaxDice(int maxDice) throws Exception {
if(getMinDice() > maxDice){
throw new Exception("Cant have minimum dice number greater than the larger dice number");
} else {
this.maxDice = maxDice;
}
}
public int getWhitePortalStart() {
return whitePortalStart;
}
public void setWhitePortalStart(int whitePortalStart) throws Exception {
this.whitePortalStart = whitePortalStart;
}
public int getWhitePortalEnd() {
return whitePortalEnd;
}
public void setWhitePortalEnd(int whitePortalEnd) throws Exception {
this.whitePortalEnd = whitePortalEnd;
}
public int getBlackPortalStart() {
return blackPortalStart;
}
public void setBlackPortalStart(int blackPortalStart) throws Exception {
this.blackPortalStart = blackPortalStart;
}
public int getBlackPortalEnd() {
return blackPortalEnd;
}
public void setBlackPortalEnd(int blackPortalEnd) throws Exception {
this.blackPortalEnd = blackPortalEnd;
}
public GameObject builder(int n) throws Exception {
setTotalSize(n);
return this;
}
public GameObject whitePortal(int m, int o) throws Exception {
setWhitePortalStart(m);
setWhitePortalEnd(o);
return this;
}
public GameObject blackPortal(int o, int m) throws Exception {
setBlackPortalStart(o);
setBlackPortalEnd(m);
return this;
}
public GameObject dice(int i, int j) throws Exception {
setMinDice(i);
setMaxDice(j);
return this;
}
public int rollDice(){
Random random = new Random();
int min = getMinDice();
int max = getMaxDice();
return random.nextInt(max - min + 1) + min;
}
public void build(){
int totalDistance = getTotalSize();
currentPosition = startPosition;
while(currentPosition < totalDistance){
int diceValue = rollDice();
if(currentPosition + diceValue > getTotalSize()){
System.out.println("CurrentPosition : " + (currentPosition + diceValue) + ", is larger than the total size of the road - " + totalSize);
continue;
} else if(currentPosition + diceValue == getWhitePortalStart()){
System.out.println("You landed on a white portal. Advancing from position " + (currentPosition + diceValue) + " to " + getWhitePortalEnd());
currentPosition = getWhitePortalEnd();
} else if(currentPosition + diceValue == getBlackPortalStart()){
System.out.println("You landed on a black portal. Moving from position " + (currentPosition + diceValue) + " to " + getBlackPortalEnd());
currentPosition = getBlackPortalEnd();
} else {
System.out.println("You landed on " + (currentPosition + diceValue));
currentPosition += diceValue;
}
}
}
So in my main method I call the it like create and call this class like;
WorldOfOz oz = new WorldOfOz();
oz.go.builder(30)
.dice(1, 4)
.whitePortal(5, 12)
.blackPortal(13, 2)
.build();
My issue is when I want to add in more than 1 whitePortal/blackPortal
WorldOfOz oz = new WorldOfOz();
oz.go.builder(30)
.dice(1, 4)
.whitePortal(5, 12)
.whitePortal(18, 26)
.blackPortal(13, 2)
.build();
The values 18 - 26 override 5 - 12. How can I set this up so I can have multiple white and black portals?
It seems that your data structure is not enough to solve this problem.
You need to define a collection of whitePortals and a collection of blackPortals. If you do so calling the method whitePortal(5, 12) add a new white portal insted of setting the white portal values of the only white existing portal.
You need to define a class Portal
public class Portal {
private int portalStart;
private int portalEnd;
...
public Portal(int s, int e) {
this.portalStart = s;
this.portalEnd = e;
}
}
Then you can use it in the GameObject as follow:
public GameObject {
List<Portal> whitePortals;
List<Portal> blackPortals;
public GameObject() {
whitePortals = new ArrayList<Portal>();
blackPortals = new ArrayList<Portal>();
}
public GameObject addWhitePortal(int m, int o) throws Exception {
whitePortals.add(new Portal(m, o));
return this;
}
...
// You need to change other methods to follow a different data structure
}
Well, you can use the following approach:
Introduce a new "Portal" type with start/end attributes
Replace white/black portal attributes in your class with lists for white and black portals (or any other type of collection you like)
Replace getWhite/Black methods with access to lists
Refactor whitePortal and blackPortal method to create new instances of a portal object and add them to an appropriate collection
You can, of course, use arrays instead of collections, but that's a bit more cumbersome.
Also, assuming portals are collections, you probably need to add helper methods for operating on those. Depending on what your actual needs are.
public class Portal
{
private int start;
private int end;
public Portal(int start, int end) { ... }
public getStart() {...}
public getEnd() {...}
public setStart(int end) {...}
public setEnd(int start) {...}
}
public class GameObject
{
...
private List<Portal> whitePortals = new ArrayList<Portal>();
private List<Portal> blackPortals = new ArrayList<Portal>();
...
public GameObject whitePortal(int m, int o) throws Exception {
whitePortals.add(new Portal(m, o));
return this;
}
public GameObject blackPortal(int o, int m) throws Exception {
blackPortals.add(new Portal(m, o));
return this;
}
...
}

Words Counter using Dictionary

For homework I have to implement a Words Counter using a Dictionary.I have an inner class named Couple
protected class Couple
{ public Coppia(String key, Integer value)
{ setKey(key);
setValue(value);
}
public String toString()
{ return String.format("%-15s", key) + " | " + value; }
public String getKey()
{ return key; }
public Integer getValue()
{ return value; }
public void setKey(String key)
{ this.key = key; }
public void setValue(Integer value)
{ this.value = value; }
//campi di esemplare
private String key;
private Integer value;
}
}
I created the class WordsCounter with put,find,remove methods
class WordsCounter
{ private Couple [] a;
private int inputSize;
public WordCounter()
{ a=new Couple [10];
inputSize=0;
}
public boolean isEmpty()
{return inputSize==0;}
public int size()
{return inputSize;}
public String toString()
{String s="";
for(int i=0;i<inputSize;i++)
{s=s+a[i].toString()+"\n";
}
return s;
}
public void put(Comparable key, Comparable value)
{if(inputSize==a.length) //resize
{ Coppia [] newA=new Coppia [2*inputSize];
for(int i=0;i<inputSize;i++)
{ newA[i]=a[i];
}
a=newA;
}
for(int i=0;i<inputSize;i++) // if the word is already in, i replace the old value with the new one
{ if(a[i].getKey().equals((String)key))
{ a[i].setValue((Integer)value);
return;
}
}
//otherwise i add the word in the array
a[inputSize++]=new Couple((String)key,(Integer)value);
mergeSort(a,inputSize);
}
public void remove(Comparable key)
{ int pos=binarySearch(a,0,inputSize-1,(String)key);
if(pos<0)
{ throw new MapItemNotFoundException();
}
else
{ a[pos]=a[inputSize-1];
inputSize--;
mergeSort(a,inputSize);
}
}
public int binarySearch(Couple [] a,int start,int end,String k)
{ if(start>end)
return -1;
int mid=(start+end)/2;
if(a[mid].equals(k))
{ return mid;}
else if(k.compareTo(a[mid].getKey())<0)
{ return binarySearch(a,start,mid-1,k);
}
else if( k.compareTo(a[mid].getKey())>0)
{ return binarySearch(a,mid+1,end,k);
}
else return -1;
}
public Comparable find(Comparable key)
{ int pos=binarySearch(a,0,inputSize-1,(String)key);
if(pos<0) // the word isn't inside the array ,so i throw an exception
{ throw new MapItemNotFoundException();
}
else
{ return a[pos].getValue();
}
}
public void mergeSort(Couple [] a,int input)
{ if(input<2)
return;
int mid=input/2;
Coppia [] first=new Coppia [mid];
Coppia [] second=new Coppia [input-mid];
for(int i=0;i<first.length;i++)
{ first[i]=a[i];
}
for(int i=0;i<second.length;i++)
{ second[i]=a[i+first.length];
}
mergeSort(first,mid);
mergeSort(second,input-mid);
merges(a,first,second);
}
public void merges(Couple [] a,Couple [] b,Couple [] c)
{ int i=0;
int k=0;
int j=0;
while(i<b.length&&j<c.length)
{ if(b[k].getKey().compareTo(c[j].getKey())<0)
{ a[i++]=b[k++];
}
else
{ a[i++]=c[j++];
}
}
while(i<b.length)
{ a[i++]=b[k++];
}
while(j<c.length)
{ a[i++]=c[j++];
}
}
The main class is
public class ContatoreParoleTester
{
public static void main(String[] args)
{
FileReader read=null;
try{ read=new FileReader(in.nextLine());
}
catch(IOException e)
{
}
Scanner c=new Scanner(read);
WordCounter parole=new WordCounter();
while(c.hasNextLine())
{ Scanner token=new Scanner(c.nextLine());
token.useDelimiter("[^A-Za-z0-9]+");
while(token.hasNext())
{
String tok=token.next();
tok=tok.toLowerCase();
try{
Comparable n= parole.find(tok); // i find the word ,if it isn't inside (the method throw an exception message) i catch the exception and i insert the word in the array with value=1; else i insert it in the array with the new value
parole.put(tok,(Integer)n+1);
}
catch(MapItemNotFoundException e)
{ parole.put(tok,1);}
}
}
System.out.println(parole);
}
}
When I print the counter the variable "value" doesn't seem to update, in fact i got something like this :
word1::1
word2::1
word3::1
etc.
I think that the update method doesn't work well, but I don't know why!:(
Could anyone help me please?
Thanks
Your binary search doesn't work... It only ever returns -1. I know this because it if ever did, this line in main: parole.put(tok,(Integer)n+1); would create a ClassCastException

Handling StackOverflow in Java for Trampoline

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.

Categories