Related
I have a string like the following:
String str = "4*5";
Now I have to get the result of 20 by using the string.
I know in some other languages the eval() function will do this.
How can I do this in Java?
You can use the ScriptEngine class and evaluate it as a Javascript string.
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("js");
Object result = engine.eval("4*5");
There may be a better way, but this one works.
There is no standard Java class or method that will do what you want. Your options include:
Select and use some third-party expression evaluation library. For example JEL or any of the half dozen libraries listed here.
Wrap the expression in the Java source code for a class with an eval method, send that to the Java compiler, and then load the resulting compiled class.
Use some scripting language that can be called from Java as an expression evaluator. Possibilities include Javascript1, BeanShell, and so on. A JSR 223 compliant scripting language implementation can be called via the Scripting API.
Write your own expression evaluator from scratch.
The first approach is probably simplest. The second and third approaches are a potential security risk if you get the expression to be evaluated from an untrusted user. (Think code injection.)
1 - Javascript in Java SE is a moving target. From Java 6, a version of Mozilla's Rhino Javascript implementation was bundled with Java SE. The in Java 8, it was superseded by Nashorn. In Java 11, Nashorn was deprecated, and finally dropped from the core codebase. As of 2021, both Rhino and Nashorn are being maintained as separate (non-Oracle) products, and Oracle's GraalVM has its own Javascript implementation.
There are very few real use cases in which being able to evaluate a String as a fragment of Java code is necessary or desirable. That is, asking how to do this is really an XY problem: you actually have a different problem, which can be solved a different way.
First ask yourself, where did this String that you wish to evaluate come from? Did another part of your program generate it, or was it input provided by the user?
Another part of my program generated it: so, you want one part of your program to decide the kind of operation to perform, but not perform the operation, and a second part that performs the chosen operation. Instead of generating and then evaluating a String, use the Strategy, Command or Builder design pattern, as appropriate for your particular case.
It is user input: the user could input anything, including commands that, when executed, could cause your program to misbehave, crash, expose information that should be secret, damage persistent information (such as the content of a database), and other such nastiness. The only way to prevent that would be to parse the String yourself, check it was not malicious, and then evaluate it. But parsing it yourself is much of the work that the requested evalfunction would do, so you have saved yourself nothing. Worse still, checking that arbitrary Java was not malicious is impossible, because checking that is the halting problem.
It is user input, but the syntax and semantics of permitted text to evaluate is greatly restricted: No general purpose facility can easily implement a general purpose parser and evaluator for whatever restricted syntax and semantics you have chosen. What you need to do is implement a parser and evaluator for your chosen syntax and semantics. If the task is simple, you could write a simple recursive-descent or finite-state-machine parser by hand. If the task is difficult, you could use a compiler-compiler (such as ANTLR) to do some of the work for you.
I just want to implement a desktop calculator!: A homework assignment, eh? If you could implement the evaluation of the input expression using a provided eval function, it would not be much of a homework assignment, would it? Your program would be three lines long. Your instructor probably expects you to write the code for a simple arithmetic parser/evaluator. There is well known algorithm, shunting-yard, which you might find useful.
With Java 9, we get access to jshell, so one can write something like this:
import jdk.jshell.JShell;
import java.lang.StringBuilder;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
public class Eval {
public static void main(String[] args) throws IOException {
try(JShell js = JShell.create(); BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
js.onSnippetEvent(snip -> {
if (snip.status() == jdk.jshell.Snippet.Status.VALID) {
System.out.println("➜ " + snip.value());
}
});
System.out.print("> ");
for (String line = br.readLine(); line != null; line = br.readLine()) {
js.eval(js.sourceCodeAnalysis().analyzeCompletion(line).source());
System.out.print("> ");
}
}
}
}
Sample run:
> 1 + 2 / 4 * 3
➜ 1
> 32 * 121
➜ 3872
> 4 * 5
➜ 20
> 121 * 51
➜ 6171
>
Slightly op, but that's what Java currently has to offer
I could advise you to use Exp4j. It is easy to understand as you can see from the following example code:
Expression e = new ExpressionBuilder("3 * sin(y) - 2 / (x - 2)")
.variables("x", "y")
.build()
.setVariable("x", 2.3)
.setVariable("y", 3.14);
double result = e.evaluate();
No, you can not have a generic "eval" in Java (or any compiled language). Unless you're willing to write a Java compiler AND a JVM to be executed inside of your Java program.
Yes, you can have some library to evaluate numeric algebraic expressions like the one above - see this thread for discussion.
As previous answers, there is no standard API in Java for this.
You can add groovy jar files to your path and groovy.util.Eval.me("4*5") gets your job done.
A fun way to solve your problem could be coding an eval() function on your own!
I've done it for you!
You can use FunctionSolver library simply by typing FunctionSolver.solveByX(function,value) inside your code. The function attribute is a String which represents the function you want to solve, the value attribute is the value of the independent variable
of your function (which MUST be x).
If you want to solve a function which contains more than one independent variable, you can use FunctionSolver.solve(function,values) where the values attribute is an HashMap(String,Double) which contains all your independent attributes (as Strings) and their respective values (as Doubles).
Another piece of information: I've coded a simple version of FunctionSolver, so its supports only Math methods which return a double value and which accepts one or two double values as fields (just use FunctionSolver.usableMathMethods() if you're curious) (These methods are: bs, sin, cos, tan, atan2, sqrt, log, log10, pow, exp, min, max, copySign, signum, IEEEremainder, acos, asin, atan, cbrt, ceil, cosh, expm1, floor, hypot, log1p, nextAfter, nextDown, nextUp, random, rint, sinh, tanh, toDegrees, toRadians, ulp). Also, that library supports the following operators: * / + - ^ (even if java normally does not support the ^ operator).
One last thing: while creating this library I had to use reflections to call Math methods. I think it's really cool, just have a look at this if you are interested in!
That's all, here it is the code (and the library):
package core;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
public abstract class FunctionSolver {
public static double solveNumericExpression (String expression) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
return solve(expression, new HashMap<>());
}
public static double solveByX (String function, double value) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
HashMap<String, Double> values = new HashMap<>();
values.put("x", value);
return solveComplexFunction(function, function, values);
}
public static double solve (String function, HashMap<String,Double> values) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
return solveComplexFunction(function, function, values);
}
private static double solveComplexFunction (String function, String motherFunction, HashMap<String, Double> values) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
int position = 0;
while(position < function.length()) {
if (alphabetic.contains(""+function.charAt(position))) {
if (position == 0 || !alphabetic.contains(""+function.charAt(position-1))) {
int endIndex = -1;
for (int j = position ; j < function.length()-1 ; j++) {
if (alphabetic.contains(""+function.charAt(j))
&& !alphabetic.contains(""+function.charAt(j+1))) {
endIndex = j;
break;
}
}
if (endIndex == -1 & alphabetic.contains(""+function.charAt(function.length()-1))) {
endIndex = function.length()-1;
}
if (endIndex != -1) {
String alphabeticElement = function.substring(position, endIndex+1);
if (Arrays.asList(usableMathMethods()).contains(alphabeticElement)) {
//Start analyzing a Math function
int closeParenthesisIndex = -1;
int openedParenthesisquantity = 0;
int commaIndex = -1;
for (int j = endIndex+1 ; j < function.length() ; j++) {
if (function.substring(j,j+1).equals("(")) {
openedParenthesisquantity++;
}else if (function.substring(j,j+1).equals(")")) {
openedParenthesisquantity--;
if (openedParenthesisquantity == 0) {
closeParenthesisIndex = j;
break;
}
}else if (function.substring(j,j+1).equals(",") & openedParenthesisquantity == 0) {
if (commaIndex == -1) {
commaIndex = j;
}else{
throw new IllegalArgumentException("The argument of math function (which is "+alphabeticElement+") has too many commas");
}
}
}
if (closeParenthesisIndex == -1) {
throw new IllegalArgumentException("The argument of a Math function (which is "+alphabeticElement+") hasn't got the closing bracket )");
}
String functionArgument = function.substring(endIndex+2,closeParenthesisIndex);
if (commaIndex != -1) {
double firstParameter = solveComplexFunction(functionArgument.substring(0,commaIndex),motherFunction,values);
double secondParameter = solveComplexFunction(functionArgument.substring(commaIndex+1),motherFunction,values);
Method mathMethod = Math.class.getDeclaredMethod(alphabeticElement, new Class<?>[] {double.class, double.class});
mathMethod.setAccessible(true);
String newKey = getNewKey(values);
values.put(newKey, (Double) mathMethod.invoke(null, firstParameter, secondParameter));
function = function.substring(0, position)+newKey
+((closeParenthesisIndex == function.length()-1)?(""):(function.substring(closeParenthesisIndex+1)));
}else {
double firstParameter = solveComplexFunction(functionArgument, motherFunction, values);
Method mathMethod = Math.class.getDeclaredMethod(alphabeticElement, new Class<?>[] {double.class});
mathMethod.setAccessible(true);
String newKey = getNewKey(values);
values.put(newKey, (Double) mathMethod.invoke(null, firstParameter));
function = function.substring(0, position)+newKey
+((closeParenthesisIndex == function.length()-1)?(""):(function.substring(closeParenthesisIndex+1)));
}
}else if (!values.containsKey(alphabeticElement)) {
throw new IllegalArgumentException("Found a group of letters ("+alphabeticElement+") which is neither a variable nor a Math function: ");
}
}
}
}
position++;
}
return solveBracketsFunction(function,motherFunction,values);
}
private static double solveBracketsFunction (String function,String motherFunction,HashMap<String, Double> values) throws IllegalArgumentException{
function = function.replace(" ", "");
String openingBrackets = "([{";
String closingBrackets = ")]}";
int parenthesisIndex = 0;
do {
int position = 0;
int openParenthesisBlockIndex = -1;
String currentOpeningBracket = openingBrackets.charAt(parenthesisIndex)+"";
String currentClosingBracket = closingBrackets.charAt(parenthesisIndex)+"";
if (contOccouranceIn(currentOpeningBracket,function) != contOccouranceIn(currentClosingBracket,function)) {
throw new IllegalArgumentException("Error: brackets are misused in the function "+function);
}
while (position < function.length()) {
if (function.substring(position,position+1).equals(currentOpeningBracket)) {
if (position != 0 && !operators.contains(function.substring(position-1,position))) {
throw new IllegalArgumentException("Error in function: there must be an operator following a "+currentClosingBracket+" breacket");
}
openParenthesisBlockIndex = position;
}else if (function.substring(position,position+1).equals(currentClosingBracket)) {
if (position != function.length()-1 && !operators.contains(function.substring(position+1,position+2))) {
throw new IllegalArgumentException("Error in function: there must be an operator before a "+currentClosingBracket+" breacket");
}
String newKey = getNewKey(values);
values.put(newKey, solveBracketsFunction(function.substring(openParenthesisBlockIndex+1,position),motherFunction, values));
function = function.substring(0,openParenthesisBlockIndex)+newKey
+((position == function.length()-1)?(""):(function.substring(position+1)));
position = -1;
}
position++;
}
parenthesisIndex++;
}while (parenthesisIndex < openingBrackets.length());
return solveBasicFunction(function,motherFunction, values);
}
private static double solveBasicFunction (String function, String motherFunction, HashMap<String, Double> values) throws IllegalArgumentException{
if (!firstContainsOnlySecond(function, alphanumeric+operators)) {
throw new IllegalArgumentException("The function "+function+" is not a basic function");
}
if (function.contains("**") |
function.contains("//") |
function.contains("--") |
function.contains("+*") |
function.contains("+/") |
function.contains("-*") |
function.contains("-/")) {
/*
* ( -+ , +- , *- , *+ , /- , /+ )> Those values are admitted
*/
throw new IllegalArgumentException("Operators are misused in the function");
}
function = function.replace(" ", "");
int position;
int operatorIndex = 0;
String currentOperator;
do {
currentOperator = operators.substring(operatorIndex,operatorIndex+1);
if (currentOperator.equals("*")) {
currentOperator+="/";
operatorIndex++;
}else if (currentOperator.equals("+")) {
currentOperator+="-";
operatorIndex++;
}
operatorIndex++;
position = 0;
while (position < function.length()) {
if ((position == 0 && !(""+function.charAt(position)).equals("-") && !(""+function.charAt(position)).equals("+") && operators.contains(""+function.charAt(position))) ||
(position == function.length()-1 && operators.contains(""+function.charAt(position)))){
throw new IllegalArgumentException("Operators are misused in the function");
}
if (currentOperator.contains(function.substring(position, position+1)) & position != 0) {
int firstTermBeginIndex = position;
while (firstTermBeginIndex > 0) {
if ((alphanumeric.contains(""+function.charAt(firstTermBeginIndex))) & (operators.contains(""+function.charAt(firstTermBeginIndex-1)))){
break;
}
firstTermBeginIndex--;
}
if (firstTermBeginIndex != 0 && (function.charAt(firstTermBeginIndex-1) == '-' | function.charAt(firstTermBeginIndex-1) == '+')) {
if (firstTermBeginIndex == 1) {
firstTermBeginIndex--;
}else if (operators.contains(""+(function.charAt(firstTermBeginIndex-2)))){
firstTermBeginIndex--;
}
}
String firstTerm = function.substring(firstTermBeginIndex,position);
int secondTermLastIndex = position;
while (secondTermLastIndex < function.length()-1) {
if ((alphanumeric.contains(""+function.charAt(secondTermLastIndex))) & (operators.contains(""+function.charAt(secondTermLastIndex+1)))) {
break;
}
secondTermLastIndex++;
}
String secondTerm = function.substring(position+1,secondTermLastIndex+1);
double result;
switch (function.substring(position,position+1)) {
case "*": result = solveSingleValue(firstTerm,values)*solveSingleValue(secondTerm,values); break;
case "/": result = solveSingleValue(firstTerm,values)/solveSingleValue(secondTerm,values); break;
case "+": result = solveSingleValue(firstTerm,values)+solveSingleValue(secondTerm,values); break;
case "-": result = solveSingleValue(firstTerm,values)-solveSingleValue(secondTerm,values); break;
case "^": result = Math.pow(solveSingleValue(firstTerm,values),solveSingleValue(secondTerm,values)); break;
default: throw new IllegalArgumentException("Unknown operator: "+currentOperator);
}
String newAttribute = getNewKey(values);
values.put(newAttribute, result);
function = function.substring(0,firstTermBeginIndex)+newAttribute+function.substring(secondTermLastIndex+1,function.length());
deleteValueIfPossible(firstTerm, values, motherFunction);
deleteValueIfPossible(secondTerm, values, motherFunction);
position = -1;
}
position++;
}
}while (operatorIndex < operators.length());
return solveSingleValue(function, values);
}
private static double solveSingleValue (String singleValue, HashMap<String, Double> values) throws IllegalArgumentException{
if (isDouble(singleValue)) {
return Double.parseDouble(singleValue);
}else if (firstContainsOnlySecond(singleValue, alphabetic)){
return getValueFromVariable(singleValue, values);
}else if (firstContainsOnlySecond(singleValue, alphanumeric+"-+")) {
String[] composition = splitByLettersAndNumbers(singleValue);
if (composition.length != 2) {
throw new IllegalArgumentException("Wrong expression: "+singleValue);
}else {
if (composition[0].equals("-")) {
composition[0] = "-1";
}else if (composition[1].equals("-")) {
composition[1] = "-1";
}else if (composition[0].equals("+")) {
composition[0] = "+1";
}else if (composition[1].equals("+")) {
composition[1] = "+1";
}
if (isDouble(composition[0])) {
return Double.parseDouble(composition[0])*getValueFromVariable(composition[1], values);
}else if (isDouble(composition[1])){
return Double.parseDouble(composition[1])*getValueFromVariable(composition[0], values);
}else {
throw new IllegalArgumentException("Wrong expression: "+singleValue);
}
}
}else {
throw new IllegalArgumentException("Wrong expression: "+singleValue);
}
}
private static double getValueFromVariable (String variable, HashMap<String, Double> values) throws IllegalArgumentException{
Double val = values.get(variable);
if (val == null) {
throw new IllegalArgumentException("Unknown variable: "+variable);
}else {
return val;
}
}
/*
* FunctionSolver help tools:
*
*/
private static final String alphabetic = "abcdefghilmnopqrstuvzwykxy";
private static final String numeric = "0123456789.";
private static final String alphanumeric = alphabetic+numeric;
private static final String operators = "^*/+-"; //--> Operators order in important!
private static boolean firstContainsOnlySecond(String firstString, String secondString) {
for (int j = 0 ; j < firstString.length() ; j++) {
if (!secondString.contains(firstString.substring(j, j+1))) {
return false;
}
}
return true;
}
private static String getNewKey (HashMap<String, Double> hashMap) {
String alpha = "abcdefghilmnopqrstuvzyjkx";
for (int j = 0 ; j < alpha.length() ; j++) {
String k = alpha.substring(j,j+1);
if (!hashMap.containsKey(k) & !Arrays.asList(usableMathMethods()).contains(k)) {
return k;
}
}
for (int j = 0 ; j < alpha.length() ; j++) {
for (int i = 0 ; i < alpha.length() ; i++) {
String k = alpha.substring(j,j+1)+alpha.substring(i,i+1);
if (!hashMap.containsKey(k) & !Arrays.asList(usableMathMethods()).contains(k)) {
return k;
}
}
}
throw new NullPointerException();
}
public static String[] usableMathMethods () {
/*
* Only methods that:
* return a double type
* present one or two parameters (which are double type)
*/
Method[] mathMethods = Math.class.getDeclaredMethods();
ArrayList<String> usableMethodsNames = new ArrayList<>();
for (Method method : mathMethods) {
boolean usable = true;
int argumentsCounter = 0;
Class<?>[] methodParametersTypes = method.getParameterTypes();
for (Class<?> parameter : methodParametersTypes) {
if (!parameter.getSimpleName().equalsIgnoreCase("double")) {
usable = false;
break;
}else {
argumentsCounter++;
}
}
if (!method.getReturnType().getSimpleName().toLowerCase().equals("double")) {
usable = false;
}
if (usable & argumentsCounter<=2) {
usableMethodsNames.add(method.getName());
}
}
return usableMethodsNames.toArray(new String[usableMethodsNames.size()]);
}
private static boolean isDouble (String number) {
try {
Double.parseDouble(number);
return true;
}catch (Exception ex) {
return false;
}
}
private static String[] splitByLettersAndNumbers (String val) {
if (!firstContainsOnlySecond(val, alphanumeric+"+-")) {
throw new IllegalArgumentException("Wrong passed value: <<"+val+">>");
}
ArrayList<String> response = new ArrayList<>();
String searchingFor;
int lastIndex = 0;
if (firstContainsOnlySecond(""+val.charAt(0), numeric+"+-")) {
searchingFor = alphabetic;
}else {
searchingFor = numeric+"+-";
}
for (int j = 0 ; j < val.length() ; j++) {
if (searchingFor.contains(val.charAt(j)+"")) {
response.add(val.substring(lastIndex, j));
lastIndex = j;
if (searchingFor.equals(numeric+"+-")) {
searchingFor = alphabetic;
}else {
searchingFor = numeric+"+-";
}
}
}
response.add(val.substring(lastIndex,val.length()));
return response.toArray(new String[response.size()]);
}
private static void deleteValueIfPossible (String val, HashMap<String, Double> values, String function) {
if (values.get(val) != null & function != null) {
if (!function.contains(val)) {
values.remove(val);
}
}
}
private static int contOccouranceIn (String howManyOfThatString, String inThatString) {
return inThatString.length() - inThatString.replace(howManyOfThatString, "").length();
}
}
Writing your own library is not that hard as u might thing. Here is link for Shunting-yard algorithm with step by step algorithm explenation. Although, you will have to parse the input for tokens first.
There are 2 other questions wich can give you some information too:
Turn a String into a Math Expression?
What's a good library for parsing mathematical expressions in java?
As there are many answers, I'm adding my implementation on top of eval() method with some additional features like support for factorial, evaluating complex expressions etc.
package evaluation;
import java.math.BigInteger;
import java.util.EmptyStackException;
import java.util.Scanner;
import java.util.Stack;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class EvalPlus {
private static Scanner scanner = new Scanner(System.in);
public static void main(String[] args) {
System.out.println("This Evaluation is based on BODMAS rule\n");
evaluate();
}
private static void evaluate() {
StringBuilder finalStr = new StringBuilder();
System.out.println("Enter an expression to evaluate:");
String expr = scanner.nextLine();
if(isProperExpression(expr)) {
expr = replaceBefore(expr);
char[] temp = expr.toCharArray();
String operators = "(+-*/%)";
for(int i = 0; i < temp.length; i++) {
if((i == 0 && temp[i] != '*') || (i == temp.length-1 && temp[i] != '*' && temp[i] != '!')) {
finalStr.append(temp[i]);
} else if((i > 0 && i < temp.length -1) || (i==temp.length-1 && temp[i] == '!')) {
if(temp[i] == '!') {
StringBuilder str = new StringBuilder();
for(int k = i-1; k >= 0; k--) {
if(Character.isDigit(temp[k])) {
str.insert(0, temp[k] );
} else {
break;
}
}
Long prev = Long.valueOf(str.toString());
BigInteger val = new BigInteger("1");
for(Long j = prev; j > 1; j--) {
val = val.multiply(BigInteger.valueOf(j));
}
finalStr.setLength(finalStr.length() - str.length());
finalStr.append("(" + val + ")");
if(temp.length > i+1) {
char next = temp[i+1];
if(operators.indexOf(next) == -1) {
finalStr.append("*");
}
}
} else {
finalStr.append(temp[i]);
}
}
}
expr = finalStr.toString();
if(expr != null && !expr.isEmpty()) {
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("JavaScript");
try {
System.out.println("Result: " + engine.eval(expr));
evaluate();
} catch (ScriptException e) {
System.out.println(e.getMessage());
}
} else {
System.out.println("Please give an expression");
evaluate();
}
} else {
System.out.println("Not a valid expression");
evaluate();
}
}
private static String replaceBefore(String expr) {
expr = expr.replace("(", "*(");
expr = expr.replace("+*", "+").replace("-*", "-").replace("**", "*").replace("/*", "/").replace("%*", "%");
return expr;
}
private static boolean isProperExpression(String expr) {
expr = expr.replaceAll("[^()]", "");
char[] arr = expr.toCharArray();
Stack<Character> stack = new Stack<Character>();
int i =0;
while(i < arr.length) {
try {
if(arr[i] == '(') {
stack.push(arr[i]);
} else {
stack.pop();
}
} catch (EmptyStackException e) {
stack.push(arr[i]);
}
i++;
}
return stack.isEmpty();
}
}
Please find the updated gist anytime here. Also comment if any issues are there. Thanks.
There are some perfectly capable answers here. However for non-trivial script it may be desirable to retain the code in a cache, or for debugging purposes, or even to have dynamically self-updating code.
To that end, sometimes it's simpler or more robust to interact with Java via command line. Create a temporary directory, output your script and any assets, create the jar. Finally import your new code.
It's a bit beyond the scope of normal eval() use in most languages, though you could certainly implement eval by returning the result from some function in your jar.
Still, thought I'd mention this method as it does fully encapsulate everything Java can do without 3rd party tools, in case of desperation. This method allows me to turn HTML templates into objects and save them, avoiding the need to parse a template at runtime.
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
class Calculate {
public static void main(String[] args) {
String strng = "8*-2*3*-1*10/2+6-2";
String[] oparator = {"+","-","*","/"};
List<String> op1 = new ArrayList<>();
String[] x = strng.split("");
int sayac=0;
for (String i : x) {
sayac ++;
for (String c : oparator) {
if (i.equals(c)) {
try {
int j = Integer.parseInt(strng.substring(0, sayac - 1));
op1.add(strng.substring(0, sayac - 1));
op1.add(c);
strng = strng.substring(sayac);
sayac = 0;
}catch (Exception e)
{
continue;
}
}
}
}
op1.add(strng);
ListIterator<String> it = op1.listIterator();
List<List> newlist = new ArrayList<>() ;
while (it.hasNext()) {
List<String> p= new ArrayList<>();
p.add(String.valueOf(it.nextIndex()));
p.add(it.next());
newlist.add(p);
}
int sayac2=0;
String oparatorvalue = "*";
calculate(sayac2,newlist,oparatorvalue);
String oparatorvalue2 = "/";
calculate(sayac2,newlist,oparatorvalue2);
String oparatorvalue3 = "+";
calculate(sayac2,newlist,oparatorvalue3);
String oparatorvalue4 = "-";
calculate(sayac2,newlist,oparatorvalue4);
System.out.println("Result:"+newlist.get(0).get(1));
}
private static void calculate(int sayac2, List<List> newlist, String oparatorvalue) {
while (sayac2<4){
try{
for (List j : newlist) {
if (j.get(1) == oparatorvalue) {
Integer opindex = newlist.indexOf(j);
Object sayi1 = newlist.get(opindex - 1).get(1);
Object sayi2 = newlist.get(opindex + 1).get(1);
int sonuc=0;
if (oparatorvalue.equals("*")){
sonuc = Integer.parseInt(sayi1.toString()) * Integer.parseInt(sayi2.toString());
}
if (oparatorvalue.equals("/")){
sonuc = Integer.parseInt(sayi1.toString()) / Integer.parseInt(sayi2.toString());
}
if (oparatorvalue.equals("+")){
sonuc = Integer.parseInt(sayi1.toString()) + Integer.parseInt(sayi2.toString());
}
if (oparatorvalue.equals("-")){
sonuc = Integer.parseInt(sayi1.toString()) - Integer.parseInt(sayi2.toString());
}
newlist.remove(opindex - 1);
newlist.remove(opindex - 1);
newlist.remove(opindex - 1);
List<String> sonuclist = new ArrayList<>();
sonuclist.add(String.valueOf(opindex - 1));
sonuclist.add(String.valueOf(sonuc));
newlist.add(opindex - 1, sonuclist);
}}}
catch (Exception e){
continue;
}
sayac2++;}
}
}
If you do not want to import heavy scripting library, you can use SimpleExpressionEvaluator directly into your code
Usage:
Expression.eval("1+2").asString(); // returns "3.0"
Expression.eval("1+2").asInt(); // returns 3
Expression.eval("2>3").asString(); // returns "false"
Expression.eval("2>3").asBoolean(); // returns false
Expression.eval("(3>2)||((2<4)&&(2>1))").asString(); // returns "true"
With variables:
HashMap<String, Object> st = new HashMap<String, Object>();
st.put("a",1);
st.put("b",2);
st.put("c",3);
st.put("d",4);
Expression.eval("a+b", st).asInt(); // or simply asString()
Expression.eval("a>b",st).asBoolean(); // or simply asString()
Expression.eval("(c>b)||((b<d)&&(b>a))",st).asBoolean(); // or simply asString()
Expression.eval("(c>2)||((2<d)&&(b>1))",st).asBoolean(); // or simply asString()
Using ExpressionBuilder:
Expression.expressionBuilder().putSymbol("a",2).putSymbol("b",3).build("(b>a)").evaluate()
The following resolved the issue:
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("JavaScript");
String str = "4*5";
System.out.println(engine.eval(str));
I have a string like the following:
String str = "4*5";
Now I have to get the result of 20 by using the string.
I know in some other languages the eval() function will do this.
How can I do this in Java?
You can use the ScriptEngine class and evaluate it as a Javascript string.
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("js");
Object result = engine.eval("4*5");
There may be a better way, but this one works.
There is no standard Java class or method that will do what you want. Your options include:
Select and use some third-party expression evaluation library. For example JEL or any of the half dozen libraries listed here.
Wrap the expression in the Java source code for a class with an eval method, send that to the Java compiler, and then load the resulting compiled class.
Use some scripting language that can be called from Java as an expression evaluator. Possibilities include Javascript1, BeanShell, and so on. A JSR 223 compliant scripting language implementation can be called via the Scripting API.
Write your own expression evaluator from scratch.
The first approach is probably simplest. The second and third approaches are a potential security risk if you get the expression to be evaluated from an untrusted user. (Think code injection.)
1 - Javascript in Java SE is a moving target. From Java 6, a version of Mozilla's Rhino Javascript implementation was bundled with Java SE. The in Java 8, it was superseded by Nashorn. In Java 11, Nashorn was deprecated, and finally dropped from the core codebase. As of 2021, both Rhino and Nashorn are being maintained as separate (non-Oracle) products, and Oracle's GraalVM has its own Javascript implementation.
There are very few real use cases in which being able to evaluate a String as a fragment of Java code is necessary or desirable. That is, asking how to do this is really an XY problem: you actually have a different problem, which can be solved a different way.
First ask yourself, where did this String that you wish to evaluate come from? Did another part of your program generate it, or was it input provided by the user?
Another part of my program generated it: so, you want one part of your program to decide the kind of operation to perform, but not perform the operation, and a second part that performs the chosen operation. Instead of generating and then evaluating a String, use the Strategy, Command or Builder design pattern, as appropriate for your particular case.
It is user input: the user could input anything, including commands that, when executed, could cause your program to misbehave, crash, expose information that should be secret, damage persistent information (such as the content of a database), and other such nastiness. The only way to prevent that would be to parse the String yourself, check it was not malicious, and then evaluate it. But parsing it yourself is much of the work that the requested evalfunction would do, so you have saved yourself nothing. Worse still, checking that arbitrary Java was not malicious is impossible, because checking that is the halting problem.
It is user input, but the syntax and semantics of permitted text to evaluate is greatly restricted: No general purpose facility can easily implement a general purpose parser and evaluator for whatever restricted syntax and semantics you have chosen. What you need to do is implement a parser and evaluator for your chosen syntax and semantics. If the task is simple, you could write a simple recursive-descent or finite-state-machine parser by hand. If the task is difficult, you could use a compiler-compiler (such as ANTLR) to do some of the work for you.
I just want to implement a desktop calculator!: A homework assignment, eh? If you could implement the evaluation of the input expression using a provided eval function, it would not be much of a homework assignment, would it? Your program would be three lines long. Your instructor probably expects you to write the code for a simple arithmetic parser/evaluator. There is well known algorithm, shunting-yard, which you might find useful.
With Java 9, we get access to jshell, so one can write something like this:
import jdk.jshell.JShell;
import java.lang.StringBuilder;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;
public class Eval {
public static void main(String[] args) throws IOException {
try(JShell js = JShell.create(); BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
js.onSnippetEvent(snip -> {
if (snip.status() == jdk.jshell.Snippet.Status.VALID) {
System.out.println("➜ " + snip.value());
}
});
System.out.print("> ");
for (String line = br.readLine(); line != null; line = br.readLine()) {
js.eval(js.sourceCodeAnalysis().analyzeCompletion(line).source());
System.out.print("> ");
}
}
}
}
Sample run:
> 1 + 2 / 4 * 3
➜ 1
> 32 * 121
➜ 3872
> 4 * 5
➜ 20
> 121 * 51
➜ 6171
>
Slightly op, but that's what Java currently has to offer
I could advise you to use Exp4j. It is easy to understand as you can see from the following example code:
Expression e = new ExpressionBuilder("3 * sin(y) - 2 / (x - 2)")
.variables("x", "y")
.build()
.setVariable("x", 2.3)
.setVariable("y", 3.14);
double result = e.evaluate();
No, you can not have a generic "eval" in Java (or any compiled language). Unless you're willing to write a Java compiler AND a JVM to be executed inside of your Java program.
Yes, you can have some library to evaluate numeric algebraic expressions like the one above - see this thread for discussion.
As previous answers, there is no standard API in Java for this.
You can add groovy jar files to your path and groovy.util.Eval.me("4*5") gets your job done.
A fun way to solve your problem could be coding an eval() function on your own!
I've done it for you!
You can use FunctionSolver library simply by typing FunctionSolver.solveByX(function,value) inside your code. The function attribute is a String which represents the function you want to solve, the value attribute is the value of the independent variable
of your function (which MUST be x).
If you want to solve a function which contains more than one independent variable, you can use FunctionSolver.solve(function,values) where the values attribute is an HashMap(String,Double) which contains all your independent attributes (as Strings) and their respective values (as Doubles).
Another piece of information: I've coded a simple version of FunctionSolver, so its supports only Math methods which return a double value and which accepts one or two double values as fields (just use FunctionSolver.usableMathMethods() if you're curious) (These methods are: bs, sin, cos, tan, atan2, sqrt, log, log10, pow, exp, min, max, copySign, signum, IEEEremainder, acos, asin, atan, cbrt, ceil, cosh, expm1, floor, hypot, log1p, nextAfter, nextDown, nextUp, random, rint, sinh, tanh, toDegrees, toRadians, ulp). Also, that library supports the following operators: * / + - ^ (even if java normally does not support the ^ operator).
One last thing: while creating this library I had to use reflections to call Math methods. I think it's really cool, just have a look at this if you are interested in!
That's all, here it is the code (and the library):
package core;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
public abstract class FunctionSolver {
public static double solveNumericExpression (String expression) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
return solve(expression, new HashMap<>());
}
public static double solveByX (String function, double value) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
HashMap<String, Double> values = new HashMap<>();
values.put("x", value);
return solveComplexFunction(function, function, values);
}
public static double solve (String function, HashMap<String,Double> values) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
return solveComplexFunction(function, function, values);
}
private static double solveComplexFunction (String function, String motherFunction, HashMap<String, Double> values) throws NoSuchMethodException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
int position = 0;
while(position < function.length()) {
if (alphabetic.contains(""+function.charAt(position))) {
if (position == 0 || !alphabetic.contains(""+function.charAt(position-1))) {
int endIndex = -1;
for (int j = position ; j < function.length()-1 ; j++) {
if (alphabetic.contains(""+function.charAt(j))
&& !alphabetic.contains(""+function.charAt(j+1))) {
endIndex = j;
break;
}
}
if (endIndex == -1 & alphabetic.contains(""+function.charAt(function.length()-1))) {
endIndex = function.length()-1;
}
if (endIndex != -1) {
String alphabeticElement = function.substring(position, endIndex+1);
if (Arrays.asList(usableMathMethods()).contains(alphabeticElement)) {
//Start analyzing a Math function
int closeParenthesisIndex = -1;
int openedParenthesisquantity = 0;
int commaIndex = -1;
for (int j = endIndex+1 ; j < function.length() ; j++) {
if (function.substring(j,j+1).equals("(")) {
openedParenthesisquantity++;
}else if (function.substring(j,j+1).equals(")")) {
openedParenthesisquantity--;
if (openedParenthesisquantity == 0) {
closeParenthesisIndex = j;
break;
}
}else if (function.substring(j,j+1).equals(",") & openedParenthesisquantity == 0) {
if (commaIndex == -1) {
commaIndex = j;
}else{
throw new IllegalArgumentException("The argument of math function (which is "+alphabeticElement+") has too many commas");
}
}
}
if (closeParenthesisIndex == -1) {
throw new IllegalArgumentException("The argument of a Math function (which is "+alphabeticElement+") hasn't got the closing bracket )");
}
String functionArgument = function.substring(endIndex+2,closeParenthesisIndex);
if (commaIndex != -1) {
double firstParameter = solveComplexFunction(functionArgument.substring(0,commaIndex),motherFunction,values);
double secondParameter = solveComplexFunction(functionArgument.substring(commaIndex+1),motherFunction,values);
Method mathMethod = Math.class.getDeclaredMethod(alphabeticElement, new Class<?>[] {double.class, double.class});
mathMethod.setAccessible(true);
String newKey = getNewKey(values);
values.put(newKey, (Double) mathMethod.invoke(null, firstParameter, secondParameter));
function = function.substring(0, position)+newKey
+((closeParenthesisIndex == function.length()-1)?(""):(function.substring(closeParenthesisIndex+1)));
}else {
double firstParameter = solveComplexFunction(functionArgument, motherFunction, values);
Method mathMethod = Math.class.getDeclaredMethod(alphabeticElement, new Class<?>[] {double.class});
mathMethod.setAccessible(true);
String newKey = getNewKey(values);
values.put(newKey, (Double) mathMethod.invoke(null, firstParameter));
function = function.substring(0, position)+newKey
+((closeParenthesisIndex == function.length()-1)?(""):(function.substring(closeParenthesisIndex+1)));
}
}else if (!values.containsKey(alphabeticElement)) {
throw new IllegalArgumentException("Found a group of letters ("+alphabeticElement+") which is neither a variable nor a Math function: ");
}
}
}
}
position++;
}
return solveBracketsFunction(function,motherFunction,values);
}
private static double solveBracketsFunction (String function,String motherFunction,HashMap<String, Double> values) throws IllegalArgumentException{
function = function.replace(" ", "");
String openingBrackets = "([{";
String closingBrackets = ")]}";
int parenthesisIndex = 0;
do {
int position = 0;
int openParenthesisBlockIndex = -1;
String currentOpeningBracket = openingBrackets.charAt(parenthesisIndex)+"";
String currentClosingBracket = closingBrackets.charAt(parenthesisIndex)+"";
if (contOccouranceIn(currentOpeningBracket,function) != contOccouranceIn(currentClosingBracket,function)) {
throw new IllegalArgumentException("Error: brackets are misused in the function "+function);
}
while (position < function.length()) {
if (function.substring(position,position+1).equals(currentOpeningBracket)) {
if (position != 0 && !operators.contains(function.substring(position-1,position))) {
throw new IllegalArgumentException("Error in function: there must be an operator following a "+currentClosingBracket+" breacket");
}
openParenthesisBlockIndex = position;
}else if (function.substring(position,position+1).equals(currentClosingBracket)) {
if (position != function.length()-1 && !operators.contains(function.substring(position+1,position+2))) {
throw new IllegalArgumentException("Error in function: there must be an operator before a "+currentClosingBracket+" breacket");
}
String newKey = getNewKey(values);
values.put(newKey, solveBracketsFunction(function.substring(openParenthesisBlockIndex+1,position),motherFunction, values));
function = function.substring(0,openParenthesisBlockIndex)+newKey
+((position == function.length()-1)?(""):(function.substring(position+1)));
position = -1;
}
position++;
}
parenthesisIndex++;
}while (parenthesisIndex < openingBrackets.length());
return solveBasicFunction(function,motherFunction, values);
}
private static double solveBasicFunction (String function, String motherFunction, HashMap<String, Double> values) throws IllegalArgumentException{
if (!firstContainsOnlySecond(function, alphanumeric+operators)) {
throw new IllegalArgumentException("The function "+function+" is not a basic function");
}
if (function.contains("**") |
function.contains("//") |
function.contains("--") |
function.contains("+*") |
function.contains("+/") |
function.contains("-*") |
function.contains("-/")) {
/*
* ( -+ , +- , *- , *+ , /- , /+ )> Those values are admitted
*/
throw new IllegalArgumentException("Operators are misused in the function");
}
function = function.replace(" ", "");
int position;
int operatorIndex = 0;
String currentOperator;
do {
currentOperator = operators.substring(operatorIndex,operatorIndex+1);
if (currentOperator.equals("*")) {
currentOperator+="/";
operatorIndex++;
}else if (currentOperator.equals("+")) {
currentOperator+="-";
operatorIndex++;
}
operatorIndex++;
position = 0;
while (position < function.length()) {
if ((position == 0 && !(""+function.charAt(position)).equals("-") && !(""+function.charAt(position)).equals("+") && operators.contains(""+function.charAt(position))) ||
(position == function.length()-1 && operators.contains(""+function.charAt(position)))){
throw new IllegalArgumentException("Operators are misused in the function");
}
if (currentOperator.contains(function.substring(position, position+1)) & position != 0) {
int firstTermBeginIndex = position;
while (firstTermBeginIndex > 0) {
if ((alphanumeric.contains(""+function.charAt(firstTermBeginIndex))) & (operators.contains(""+function.charAt(firstTermBeginIndex-1)))){
break;
}
firstTermBeginIndex--;
}
if (firstTermBeginIndex != 0 && (function.charAt(firstTermBeginIndex-1) == '-' | function.charAt(firstTermBeginIndex-1) == '+')) {
if (firstTermBeginIndex == 1) {
firstTermBeginIndex--;
}else if (operators.contains(""+(function.charAt(firstTermBeginIndex-2)))){
firstTermBeginIndex--;
}
}
String firstTerm = function.substring(firstTermBeginIndex,position);
int secondTermLastIndex = position;
while (secondTermLastIndex < function.length()-1) {
if ((alphanumeric.contains(""+function.charAt(secondTermLastIndex))) & (operators.contains(""+function.charAt(secondTermLastIndex+1)))) {
break;
}
secondTermLastIndex++;
}
String secondTerm = function.substring(position+1,secondTermLastIndex+1);
double result;
switch (function.substring(position,position+1)) {
case "*": result = solveSingleValue(firstTerm,values)*solveSingleValue(secondTerm,values); break;
case "/": result = solveSingleValue(firstTerm,values)/solveSingleValue(secondTerm,values); break;
case "+": result = solveSingleValue(firstTerm,values)+solveSingleValue(secondTerm,values); break;
case "-": result = solveSingleValue(firstTerm,values)-solveSingleValue(secondTerm,values); break;
case "^": result = Math.pow(solveSingleValue(firstTerm,values),solveSingleValue(secondTerm,values)); break;
default: throw new IllegalArgumentException("Unknown operator: "+currentOperator);
}
String newAttribute = getNewKey(values);
values.put(newAttribute, result);
function = function.substring(0,firstTermBeginIndex)+newAttribute+function.substring(secondTermLastIndex+1,function.length());
deleteValueIfPossible(firstTerm, values, motherFunction);
deleteValueIfPossible(secondTerm, values, motherFunction);
position = -1;
}
position++;
}
}while (operatorIndex < operators.length());
return solveSingleValue(function, values);
}
private static double solveSingleValue (String singleValue, HashMap<String, Double> values) throws IllegalArgumentException{
if (isDouble(singleValue)) {
return Double.parseDouble(singleValue);
}else if (firstContainsOnlySecond(singleValue, alphabetic)){
return getValueFromVariable(singleValue, values);
}else if (firstContainsOnlySecond(singleValue, alphanumeric+"-+")) {
String[] composition = splitByLettersAndNumbers(singleValue);
if (composition.length != 2) {
throw new IllegalArgumentException("Wrong expression: "+singleValue);
}else {
if (composition[0].equals("-")) {
composition[0] = "-1";
}else if (composition[1].equals("-")) {
composition[1] = "-1";
}else if (composition[0].equals("+")) {
composition[0] = "+1";
}else if (composition[1].equals("+")) {
composition[1] = "+1";
}
if (isDouble(composition[0])) {
return Double.parseDouble(composition[0])*getValueFromVariable(composition[1], values);
}else if (isDouble(composition[1])){
return Double.parseDouble(composition[1])*getValueFromVariable(composition[0], values);
}else {
throw new IllegalArgumentException("Wrong expression: "+singleValue);
}
}
}else {
throw new IllegalArgumentException("Wrong expression: "+singleValue);
}
}
private static double getValueFromVariable (String variable, HashMap<String, Double> values) throws IllegalArgumentException{
Double val = values.get(variable);
if (val == null) {
throw new IllegalArgumentException("Unknown variable: "+variable);
}else {
return val;
}
}
/*
* FunctionSolver help tools:
*
*/
private static final String alphabetic = "abcdefghilmnopqrstuvzwykxy";
private static final String numeric = "0123456789.";
private static final String alphanumeric = alphabetic+numeric;
private static final String operators = "^*/+-"; //--> Operators order in important!
private static boolean firstContainsOnlySecond(String firstString, String secondString) {
for (int j = 0 ; j < firstString.length() ; j++) {
if (!secondString.contains(firstString.substring(j, j+1))) {
return false;
}
}
return true;
}
private static String getNewKey (HashMap<String, Double> hashMap) {
String alpha = "abcdefghilmnopqrstuvzyjkx";
for (int j = 0 ; j < alpha.length() ; j++) {
String k = alpha.substring(j,j+1);
if (!hashMap.containsKey(k) & !Arrays.asList(usableMathMethods()).contains(k)) {
return k;
}
}
for (int j = 0 ; j < alpha.length() ; j++) {
for (int i = 0 ; i < alpha.length() ; i++) {
String k = alpha.substring(j,j+1)+alpha.substring(i,i+1);
if (!hashMap.containsKey(k) & !Arrays.asList(usableMathMethods()).contains(k)) {
return k;
}
}
}
throw new NullPointerException();
}
public static String[] usableMathMethods () {
/*
* Only methods that:
* return a double type
* present one or two parameters (which are double type)
*/
Method[] mathMethods = Math.class.getDeclaredMethods();
ArrayList<String> usableMethodsNames = new ArrayList<>();
for (Method method : mathMethods) {
boolean usable = true;
int argumentsCounter = 0;
Class<?>[] methodParametersTypes = method.getParameterTypes();
for (Class<?> parameter : methodParametersTypes) {
if (!parameter.getSimpleName().equalsIgnoreCase("double")) {
usable = false;
break;
}else {
argumentsCounter++;
}
}
if (!method.getReturnType().getSimpleName().toLowerCase().equals("double")) {
usable = false;
}
if (usable & argumentsCounter<=2) {
usableMethodsNames.add(method.getName());
}
}
return usableMethodsNames.toArray(new String[usableMethodsNames.size()]);
}
private static boolean isDouble (String number) {
try {
Double.parseDouble(number);
return true;
}catch (Exception ex) {
return false;
}
}
private static String[] splitByLettersAndNumbers (String val) {
if (!firstContainsOnlySecond(val, alphanumeric+"+-")) {
throw new IllegalArgumentException("Wrong passed value: <<"+val+">>");
}
ArrayList<String> response = new ArrayList<>();
String searchingFor;
int lastIndex = 0;
if (firstContainsOnlySecond(""+val.charAt(0), numeric+"+-")) {
searchingFor = alphabetic;
}else {
searchingFor = numeric+"+-";
}
for (int j = 0 ; j < val.length() ; j++) {
if (searchingFor.contains(val.charAt(j)+"")) {
response.add(val.substring(lastIndex, j));
lastIndex = j;
if (searchingFor.equals(numeric+"+-")) {
searchingFor = alphabetic;
}else {
searchingFor = numeric+"+-";
}
}
}
response.add(val.substring(lastIndex,val.length()));
return response.toArray(new String[response.size()]);
}
private static void deleteValueIfPossible (String val, HashMap<String, Double> values, String function) {
if (values.get(val) != null & function != null) {
if (!function.contains(val)) {
values.remove(val);
}
}
}
private static int contOccouranceIn (String howManyOfThatString, String inThatString) {
return inThatString.length() - inThatString.replace(howManyOfThatString, "").length();
}
}
Writing your own library is not that hard as u might thing. Here is link for Shunting-yard algorithm with step by step algorithm explenation. Although, you will have to parse the input for tokens first.
There are 2 other questions wich can give you some information too:
Turn a String into a Math Expression?
What's a good library for parsing mathematical expressions in java?
As there are many answers, I'm adding my implementation on top of eval() method with some additional features like support for factorial, evaluating complex expressions etc.
package evaluation;
import java.math.BigInteger;
import java.util.EmptyStackException;
import java.util.Scanner;
import java.util.Stack;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class EvalPlus {
private static Scanner scanner = new Scanner(System.in);
public static void main(String[] args) {
System.out.println("This Evaluation is based on BODMAS rule\n");
evaluate();
}
private static void evaluate() {
StringBuilder finalStr = new StringBuilder();
System.out.println("Enter an expression to evaluate:");
String expr = scanner.nextLine();
if(isProperExpression(expr)) {
expr = replaceBefore(expr);
char[] temp = expr.toCharArray();
String operators = "(+-*/%)";
for(int i = 0; i < temp.length; i++) {
if((i == 0 && temp[i] != '*') || (i == temp.length-1 && temp[i] != '*' && temp[i] != '!')) {
finalStr.append(temp[i]);
} else if((i > 0 && i < temp.length -1) || (i==temp.length-1 && temp[i] == '!')) {
if(temp[i] == '!') {
StringBuilder str = new StringBuilder();
for(int k = i-1; k >= 0; k--) {
if(Character.isDigit(temp[k])) {
str.insert(0, temp[k] );
} else {
break;
}
}
Long prev = Long.valueOf(str.toString());
BigInteger val = new BigInteger("1");
for(Long j = prev; j > 1; j--) {
val = val.multiply(BigInteger.valueOf(j));
}
finalStr.setLength(finalStr.length() - str.length());
finalStr.append("(" + val + ")");
if(temp.length > i+1) {
char next = temp[i+1];
if(operators.indexOf(next) == -1) {
finalStr.append("*");
}
}
} else {
finalStr.append(temp[i]);
}
}
}
expr = finalStr.toString();
if(expr != null && !expr.isEmpty()) {
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("JavaScript");
try {
System.out.println("Result: " + engine.eval(expr));
evaluate();
} catch (ScriptException e) {
System.out.println(e.getMessage());
}
} else {
System.out.println("Please give an expression");
evaluate();
}
} else {
System.out.println("Not a valid expression");
evaluate();
}
}
private static String replaceBefore(String expr) {
expr = expr.replace("(", "*(");
expr = expr.replace("+*", "+").replace("-*", "-").replace("**", "*").replace("/*", "/").replace("%*", "%");
return expr;
}
private static boolean isProperExpression(String expr) {
expr = expr.replaceAll("[^()]", "");
char[] arr = expr.toCharArray();
Stack<Character> stack = new Stack<Character>();
int i =0;
while(i < arr.length) {
try {
if(arr[i] == '(') {
stack.push(arr[i]);
} else {
stack.pop();
}
} catch (EmptyStackException e) {
stack.push(arr[i]);
}
i++;
}
return stack.isEmpty();
}
}
Please find the updated gist anytime here. Also comment if any issues are there. Thanks.
There are some perfectly capable answers here. However for non-trivial script it may be desirable to retain the code in a cache, or for debugging purposes, or even to have dynamically self-updating code.
To that end, sometimes it's simpler or more robust to interact with Java via command line. Create a temporary directory, output your script and any assets, create the jar. Finally import your new code.
It's a bit beyond the scope of normal eval() use in most languages, though you could certainly implement eval by returning the result from some function in your jar.
Still, thought I'd mention this method as it does fully encapsulate everything Java can do without 3rd party tools, in case of desperation. This method allows me to turn HTML templates into objects and save them, avoiding the need to parse a template at runtime.
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
class Calculate {
public static void main(String[] args) {
String strng = "8*-2*3*-1*10/2+6-2";
String[] oparator = {"+","-","*","/"};
List<String> op1 = new ArrayList<>();
String[] x = strng.split("");
int sayac=0;
for (String i : x) {
sayac ++;
for (String c : oparator) {
if (i.equals(c)) {
try {
int j = Integer.parseInt(strng.substring(0, sayac - 1));
op1.add(strng.substring(0, sayac - 1));
op1.add(c);
strng = strng.substring(sayac);
sayac = 0;
}catch (Exception e)
{
continue;
}
}
}
}
op1.add(strng);
ListIterator<String> it = op1.listIterator();
List<List> newlist = new ArrayList<>() ;
while (it.hasNext()) {
List<String> p= new ArrayList<>();
p.add(String.valueOf(it.nextIndex()));
p.add(it.next());
newlist.add(p);
}
int sayac2=0;
String oparatorvalue = "*";
calculate(sayac2,newlist,oparatorvalue);
String oparatorvalue2 = "/";
calculate(sayac2,newlist,oparatorvalue2);
String oparatorvalue3 = "+";
calculate(sayac2,newlist,oparatorvalue3);
String oparatorvalue4 = "-";
calculate(sayac2,newlist,oparatorvalue4);
System.out.println("Result:"+newlist.get(0).get(1));
}
private static void calculate(int sayac2, List<List> newlist, String oparatorvalue) {
while (sayac2<4){
try{
for (List j : newlist) {
if (j.get(1) == oparatorvalue) {
Integer opindex = newlist.indexOf(j);
Object sayi1 = newlist.get(opindex - 1).get(1);
Object sayi2 = newlist.get(opindex + 1).get(1);
int sonuc=0;
if (oparatorvalue.equals("*")){
sonuc = Integer.parseInt(sayi1.toString()) * Integer.parseInt(sayi2.toString());
}
if (oparatorvalue.equals("/")){
sonuc = Integer.parseInt(sayi1.toString()) / Integer.parseInt(sayi2.toString());
}
if (oparatorvalue.equals("+")){
sonuc = Integer.parseInt(sayi1.toString()) + Integer.parseInt(sayi2.toString());
}
if (oparatorvalue.equals("-")){
sonuc = Integer.parseInt(sayi1.toString()) - Integer.parseInt(sayi2.toString());
}
newlist.remove(opindex - 1);
newlist.remove(opindex - 1);
newlist.remove(opindex - 1);
List<String> sonuclist = new ArrayList<>();
sonuclist.add(String.valueOf(opindex - 1));
sonuclist.add(String.valueOf(sonuc));
newlist.add(opindex - 1, sonuclist);
}}}
catch (Exception e){
continue;
}
sayac2++;}
}
}
If you do not want to import heavy scripting library, you can use SimpleExpressionEvaluator directly into your code
Usage:
Expression.eval("1+2").asString(); // returns "3.0"
Expression.eval("1+2").asInt(); // returns 3
Expression.eval("2>3").asString(); // returns "false"
Expression.eval("2>3").asBoolean(); // returns false
Expression.eval("(3>2)||((2<4)&&(2>1))").asString(); // returns "true"
With variables:
HashMap<String, Object> st = new HashMap<String, Object>();
st.put("a",1);
st.put("b",2);
st.put("c",3);
st.put("d",4);
Expression.eval("a+b", st).asInt(); // or simply asString()
Expression.eval("a>b",st).asBoolean(); // or simply asString()
Expression.eval("(c>b)||((b<d)&&(b>a))",st).asBoolean(); // or simply asString()
Expression.eval("(c>2)||((2<d)&&(b>1))",st).asBoolean(); // or simply asString()
Using ExpressionBuilder:
Expression.expressionBuilder().putSymbol("a",2).putSymbol("b",3).build("(b>a)").evaluate()
The following resolved the issue:
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine engine = mgr.getEngineByName("JavaScript");
String str = "4*5";
System.out.println(engine.eval(str));
I'm trying to make a 2d array of an object in java. This object in java has several private variables and methods in it, but won't work. Can someone tell me why and is there a way I can fix this?
This is the exeception I keep getting for each line of code where I try to initialize and iterate through my 2d object.
"Exception in thread "main" java.lang.NullPointerException
at wumpusworld.WumpusWorldGame.main(WumpusWorldGame.java:50)
Java Result: 1"
Here is my main class:
public class WumpusWorldGame {
class Agent {
private boolean safe;
private boolean stench;
private boolean breeze;
public Agent() {
safe = false;
stench = false;
breeze = false;
}
}
/**
* #param args
* the command line arguments
* #throws java.lang.Exception
*/
public static void main(String [] args) {
// WumpusFrame blah =new WumpusFrame();
// blah.setVisible(true);
Scanner input = new Scanner(System.in);
int agentpts = 0;
System.out.println("Welcome to Wumpus World!\n ******************************************** \n");
//ArrayList<ArrayList<WumpusWorld>> woah = new ArrayList<ArrayList<WumpusWorld>>();
for (int i = 0 ; i < 5 ; i++) {
WumpusWorldObject [] [] woah = new WumpusWorldObject [5] [5];
System.out.println( "*********************************\n Please enter the exact coordinates of the wumpus (r and c).");
int wumpusR = input.nextInt();
int wumpusC = input.nextInt();
woah[wumpusR][wumpusC].setPoints(-3000);
woah[wumpusR][wumpusC].setWumpus();
if ((wumpusR <= 5 || wumpusC <= 5) && (wumpusR >= 0 || wumpusC >= 0)) {
woah[wumpusR][wumpusC].setStench();
}
if (wumpusC != 0) {
woah[wumpusR][wumpusC - 1].getStench();
}
if (wumpusR != 0) {
woah[wumpusR - 1][wumpusC].setStench();
}
if (wumpusC != 4) {
woah[wumpusR][wumpusC + 1].setStench();
}
if (wumpusR != 4) {
woah[wumpusR + 1][wumpusC].setStench();
}
System.out.println( "**************************************\n Please enter the exact coordinates of the Gold(r and c).");
int goldR = input.nextInt();
int goldC = input.nextInt();
woah[goldR][goldC].setGold();
System.out.println("***************************************\n How many pits would you like in your wumpus world?");
int numPits = input.nextInt();
for (int k = 0 ; k < numPits ; k++) {
System.out.println("Enter the row location of the pit");
int r = input.nextInt();
System.out.println("Enter the column location of the pit");
int c = input.nextInt();
woah[r][c].setPit();
if ((r <= 4 || c <= 4) && (r >= 0 || c >= 0)) {
woah[r][c].setBreeze();
}
if (c != 0) {
woah[r][c - 1].setBreeze();
}
if (r != 0) {
woah[r - 1][c].setBreeze();
}
if (c != 4) {
woah[r][c + 1].setBreeze();
}
if (r != 4) {
woah[r + 1][c].setBreeze();
}
}
for (int x = 0 ; x < 4 ; x++) {
int j = 0;
while (j < 4) {
agentpts = agentpts + woah[x][j].getPoints();
Agent [] [] k = new Agent [4] [4];
if (woah[x][j].getWumpus() == true) {
agentpts = agentpts + woah[x][j].getPoints();
System.out.println("You just got ate by the wumpus!!! THE HORROR!! Your score is " + agentpts);
}
if (woah[x][j].getStench() == true) {
k[x][j].stench = true;
System.out.println("You smell something funny... smells like old person.");
}
if (woah[x][j].getBreeze() == true) {
k[x][j].breeze = true;
System.out.println("You hear a breeze. yeah");
}
if (woah[x][j].getPit() == true) {
agentpts = agentpts + woah[x][j].getPoints();
System.out.println("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAH! you dumb bith, your dead now.");
}
// if breeze or stench, if breeze and stench, if nothing, etc then move.
k[x][j].safe = true;
// if(k[i][j].isSafe()!=true){
// } else { }
}
}
}
}
}
Here is my class object that I'm trying to implement:
package wumpusworld;
/**
*
* #author Jacob
*/
public class WumpusWorldObject {
private boolean stench;
private boolean breeze;
private boolean pit;
private boolean wumpus;
private boolean gold;
private int points;
private boolean safe;
public WumpusWorldObject(){
}
public boolean getPit() {
return pit;
}
public void setPit() {
this.pit = true;
}
public boolean getWumpus() {
return wumpus;
}
public void setWumpus() {
this.wumpus = true;
}
public int getPoints() {
return points;
}
public void setPoints(int points) {
this.points = points;
}
public boolean getStench() {
return stench;
}
public void setStench() {
this.stench = true;
}
public boolean getBreeze() {
return breeze;
}
public void setBreeze() {
this.breeze = true;
}
public boolean getSafe() {
return safe;
}
public void setSafe() {
this.safe = true;
}
public void setGold(){
this.gold=true;
}
}
Creating array doesn't mean it will be automatically filled with new instances of your class. There are many reasons for that, like
which constructor should be used
what data should be passed to this constructor.
This kind of decisions shouldn't be made by compiler, but by programmer, so you need to invoke constructor explicitly.
After creating array iterate over it and fill it with new instances of your class.
for (int i=0; i<yourArray.length; i++)
for (int j=0; j<yourArray[i].length; j++)
yourArray[i][j] = new ...//here you should use constructor
AClass[][] obj = new AClass[50][50];
is not enough, you have to create instances of them like
obj[i][j] = new AClass(...);
In your code the line
woah[wumpusR][wumpusC].setPoints(-3000);
must be after
woah[wumpusR][wumpusC] = new WumpusWorldObject();
.
I have looked through as many previous questions as possible but never saw a question that had a boolean array as a variable.
Here is my class:
public class Register {
private boolean[] register;
private int length;
//Normal constructor
public Register(int n) {
if (n == 8 || n == 16 || n == 32 || n == 64) {
length = n;
register = new boolean[length];
for (int i = 0; i < length; i++) {
register[i] = false;
}
} else {
throw new RegisterException(
"A register can only contain 8, 16, 32, or 64 bits");
}
}
// Creates a copy of reg (an existing Register)
public Register(Register reg) {
length = reg.length;
register = new boolean[reg.register.length];
System.arraycopy(reg.register, 0, this.register, 0, reg.register.length);
}
In my driver program i am loading "1101101" into register1, but when i do:
Register register2 = new Register(register1);
and print out both results i get:
0000000001101101
0000000000010110
Not really sure what is going on O.o any help would be appreciated, thanks!
This is my load method. i held off on putting it in here because it might be hard to read:
public void load(String binaryRep) {
String allTheBits = binaryRep;
int charPosition = 0;
int loadLength;
int binaryNum = 0;
String index = "";
String trimmedIndex = "";
if (allTheBits.length() > 0 && allTheBits.length() <= length) {
loadLength = allTheBits.length();
for (int i = length - (loadLength); i < length; i++) {
index = allTheBits.charAt(charPosition) + "";
trimmedIndex = index.trim();
binaryNum = Integer.parseInt(trimmedIndex);
if (binaryNum == 1) {
register[i] = true;
} else if (binaryNum == 0) {
register[i] = false;
}
charPosition++;
}
} else {
throw new RegisterException("You can only load 0 - " + length
+ "bits.");
}
}
Here's a more idiomatic way of doing it (using the Cloneable interface):
public class Register implements Cloneable {
private boolean[] register;
public Register(boolean[] register) {
int n = register.length;
if (n == 8 || n == 16 || n == 32 || n == 64) {
this.register = register;
} else {
throw new IllegalArgumentException(
"A register can only contain 8, 16, 32, or 64 bits");
}
}
#Override
public String toString() {
StringBuilder builder = new StringBuilder();
for ( boolean b : this.register ) {
builder.append( b ? "1" : "0" );
}
return builder.toString();
}
public Register( int n ) {
this( new boolean[n] );
}
public int getLength() {
return this.register.length;
}
#Override
public Register clone() {
boolean[] clonedRegister = new boolean[this.register.length];
System.arraycopy(this.register, 0, clonedRegister,0, this.register.length);
return new Register( clonedRegister );
}
}
And a JUnit test showing it in action:
import org.junit.Assert;
import org.junit.Test;
public class RegisterTest {
#Test
public void testRegisterToString() {
Register source = new Register( new boolean[] {true, true, false, false, true, false, true, false } );
String result = "11001010";
Assert.assertEquals( result, source.toString() );
}
#Test
public void testRegisterCloning() {
Register source = new Register( new boolean[] {true, true, false, false, true, false, false, false } );
Register clone = source.clone();
Assert.assertEquals( source.toString(), clone.toString() );
}
}
A couple of remarks so that you learn some basic things.
As #Ted said, no need to keep the length field as register. length will give you the same
Local variables are not initialized with default values, but arrays are, as they are stored in the heap. So there's no need to iterate over the "register" array to set all of its positions to false
Using an array of booleans to do this may have felt easy but its extremely inefficient memory wise, as each boolean takes at least 32 bits in the heap. Therefore, to represent a 64 bit register you are using at least 32*64+32=2080 bits... using a byte array and bitwise logic will be a bit harder but hey, it's a small challenge :)
Anyway, your code looks fine (BTW, use Arrays.copyOf method as it's more readable), so the error should be coming from another side.
I just verified your load method with the following:
public static void main(String [] args)
{
Register r1 = new Register(8);
r1.load("1101101");
Register r2 = new Register(r1);
for (int i=0; i<8; i++) System.out.println(r2.register[i]);
}
Output:
> run Register
false
true
true
false
true
true
false
true
>
It looks right to me as far as the contents of the Register objects are concerned, so the problem probably is with the access.
It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 12 years ago.
Is there a way that I can optimize this code as to not run out of memory?
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Random;
import java.util.Stack;
public class TilePuzzle {
private final static byte ROWS = 4;
private final static byte COLUMNS = 4;
private static String SOLUTION = "123456789ABCDEF0";
private static byte RADIX = 16;
private char[][] board = new char[ROWS][COLUMNS];
private byte x; // Row of the space ('0')
private byte y; // Column of the space ('0') private String representation;
private boolean change = false; // Has the board changed after the last call to toString?
private TilePuzzle() {
this(SOLUTION);
int times = 1000;
Random rnd = new Random();
while(times-- > 0) {
try {
move((byte)rnd.nextInt(4));
}
catch(RuntimeException e) {
}
}
this.representation = asString();
}
public TilePuzzle(String representation) {
this.representation = representation;
final byte SIZE = (byte)SOLUTION.length();
if (representation.length() != SIZE) {
throw new IllegalArgumentException("The board must have " + SIZE + "numbers.");
}
boolean[] used = new boolean[SIZE];
byte idx = 0;
for (byte i = 0; i < ROWS; ++i) {
for (byte j = 0; j < COLUMNS; ++j) {
char digit = representation.charAt(idx++);
byte number = (byte)Character.digit(digit, RADIX);
if (number < 0 || number >= SIZE) {
throw new IllegalArgumentException("The character " + digit + " is not valid.");
} else if(used[number]) {
throw new IllegalArgumentException("The character " + digit + " is repeated.");
}
used[number] = true;
board[i][j] = digit;
if (digit == '0') {
x = i;
y = j;
}
}
}
}
/**
* Swap position of the space ('0') with the number that's up to it.
*/
public void moveUp() {
try {
move((byte)(x - 1), y);
} catch(IllegalArgumentException e) {
throw new RuntimeException("Move prohibited " + e.getMessage());
}
}
/**
* Swap position of the space ('0') with the number that's down to it.
*/
public void moveDown() {
try {
move((byte)(x + 1), y);
} catch(IllegalArgumentException e) {
throw new RuntimeException("Move prohibited " + e.getMessage());
}
}
/**
* Swap position of the space ('0') with the number that's left to it.
*/
public void moveLeft() {
try {
move(x, (byte)(y - 1));
} catch(IllegalArgumentException e) {
throw new RuntimeException("Move prohibited " + e.getMessage());
}
}
/**
* Swap position of the space ('0') with the number that's right to it.
*/
public void moveRight() {
try {
move(x, (byte)(y + 1));
} catch(IllegalArgumentException e) {
throw new RuntimeException("Move prohibited " + e.getMessage());
}
}
private void move(byte movement) {
switch(movement) {
case 0: moveUp(); break;
case 1: moveRight(); break;
case 2: moveDown(); break;
case 3: moveLeft(); break;
}
}
private boolean areValidCoordinates(byte x, byte y) {
return (x >= 0 && x < ROWS && y >= 0 && y < COLUMNS);
}
private void move(byte nx, byte ny) {
if (!areValidCoordinates(nx, ny)) {
throw new IllegalArgumentException("(" + nx + ", " + ny + ")");
}
board[x][y] = board[nx][ny];
board[nx][ny] = '0';
x = nx;
y = ny;
change = true;
}
public String printableString() {
StringBuilder sb = new StringBuilder();
for (byte i = 0; i < ROWS; ++i) {
for (byte j = 0; j < COLUMNS; ++j) {
sb.append(board[i][j] + " ");
}
sb.append("\r\n");
}
return sb.toString();
}
private String asString() {
StringBuilder sb = new StringBuilder();
for (byte i = 0; i < ROWS; ++i) {
for (byte j = 0; j < COLUMNS; ++j) {
sb.append(board[i][j]);
}
}
return sb.toString();
}
public String toString() {
if (change) {
representation = asString();
}
return representation;
}
private static byte[] whereShouldItBe(char digit) {
byte idx = (byte)SOLUTION.indexOf(digit);
return new byte[] { (byte)(idx / ROWS), (byte)(idx % ROWS) };
}
private static byte manhattanDistance(byte x, byte y, byte x2, byte y2) {
byte dx = (byte)Math.abs(x - x2);
byte dy = (byte)Math.abs(y - y2);
return (byte)(dx + dy);
}
private byte heuristic() {
byte total = 0;
for (byte i = 0; i < ROWS; ++i) {
for (byte j = 0; j < COLUMNS; ++j) {
char digit = board[i][j];
byte[] coordenates = whereShouldItBe(digit);
byte distance = manhattanDistance(i, j, coordenates[0], coordenates[1]);
total += distance;
}
}
return total;
}
private class Node implements Comparable<Node> {
private String puzzle;
private byte moves; // Number of moves from original configuration
private byte value; // The value of the heuristic for this configuration.
public Node(String puzzle, byte moves, byte value) {
this.puzzle = puzzle;
this.moves = moves;
this.value = value;
}
#Override
public int compareTo(Node o) {
return (value + moves) - (o.value + o.moves);
}
}
private void print(Map<String, String> antecessor) {
Stack toPrint = new Stack();
toPrint.add(SOLUTION);
String before = antecessor.get(SOLUTION);
while (!before.equals("")) {
toPrint.add(before);
before = antecessor.get(before);
}
while (!toPrint.isEmpty()) {
System.out.println(new TilePuzzle(toPrint.pop()).printableString());
}
}
private byte solve() {
if(toString().equals(SOLUTION)) {
return 0;
}
PriorityQueue<Node> toProcess = new PriorityQueue();
Node initial = new Node(toString(), (byte)0, heuristic());
toProcess.add(initial);
Map<String, String> antecessor = new HashMap<String, String>();
antecessor.put(toString(), "");
while(!toProcess.isEmpty()) {
Node actual = toProcess.poll();
for (byte i = 0; i < 4; ++i) {
TilePuzzle t = new TilePuzzle(actual.puzzle);
try {
t.move(i);
} catch(RuntimeException e) {
continue;
}
if (t.toString().equals(SOLUTION)) {
antecessor.put(SOLUTION, actual.puzzle);
print(antecessor);
return (byte)(actual.moves + 1);
} else if (!antecessor.containsKey(t.toString())) {
byte v = t.heuristic();
Node neighbor = new Node(t.toString(), (byte)(actual.moves + 1), v);
toProcess.add(neighbor);
antecessor.put(t.toString(), actual.puzzle);
}
}
}
return -1;
}
public static void main(String... args) {
TilePuzzle puzzle = new TilePuzzle();
System.out.println(puzzle.solve());
}
}
The problem
The root cause is the tons of String objects you are creating and storing in the toProcess Queue and the antecessor Map. Why are you doing that?
Look at your algorithm. See if you really need to store >2 million nodes and 5 million strings in each.
The investigation
This was hard to spot because the program is complex. Actually, I didn't even try to understand all of the code. Instead, I used VisualVM – a Java profiler, sampler, and CPU/memory usage monitor.
I launched it:
And took a look at the memory usage. The first thing I noticed was the (obvious) fact that you're creating tons of objects.
This is an screenshot of the app:
As you can see, the amount of memory used is tremendous. In as few as 40 seconds, 2 GB were consumed and the entire heap was filled.
A dead end
I initially thought the problem had something to do with the Node class, because even though it implements Comparable, it doesn't implement equals. So I provided the method:
public boolean equals( Object o ) {
if( o instanceof Node ) {
Node other = ( Node ) o;
return this.value == other.value && this.moves == other.moves;
}
return false;
}
But that was not the problem.
The actual problem turned out to be the one stated at the top.
The workaround
As previously stated, the real solution is to rethink your algorithm. Whatever else can be done, in the meantime, will only delay the problem.
But workarounds can be useful. One is to reuse the strings you're generating. You're very intensively using the TilePuzzle.toString() method; this ends up creating duplicate strings quite often.
Since you're generating string permutations, you may create many 12345ABCD strings in matter of seconds. If they are the same string, there is no point in creating millions of instances with the same value.
The String.intern() method allows strings to be reused. The doc says:
Returns a canonical representation for the string object.
A pool of strings, initially empty, is maintained privately by the class String.
When the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals() method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.
For a regular application, using String.intern() could be a bad idea because it doesn't let instances be reclaimed by the GC. But in this case, since you're holding the references in your Map and Queue anyway, it makes sense.
So making this change:
public String toString() {
if (change) {
representation = asString();
}
return representation.intern(); // <-- Use intern
}
Pretty much solves the memory problem.
This is a screenshot after the change:
Now, the heap usage doesn't reach 100 MB even after a couple of minutes.
Extra remarks
Remark #1
You're using an exception to validate if the movement is valid or not, which is okay; but when you catch them, you're just ignoring them:
try {
t.move(i);
} catch(RuntimeException e) {
continue;
}
If you're not using them anyway, you can save a lot of computation by not creating the exceptions in the first place. Otherwise you're creating millions of unused exceptions.
Make this change:
if (!areValidCoordinates(nx, ny)) {
// REMOVE THIS LINE:
// throw new IllegalArgumentException("(" + nx + ", " + ny + ")");
// ADD THIS LINE:
return;
}
And use validation instead:
// REMOVE THESE LINES:
// try {
// t.move(i);
// } catch(RuntimeException e) {
// continue;
// }
// ADD THESE LINES:
if(t.isValidMovement(i)){
t.move(i);
} else {
continue;
}
Remark #2
You're creating a new Random object for every new TilePuzzle instance. It would be better if you used just one for the whole program. After all, you are only using a single thread.
Remark #3
The workaround solved the heap memory problem, but created another one involving PermGen. I simply increased the PermGen size, like this:
java -Xmx1g -Xms1g -XX:MaxPermSize=1g TilePuzzle
Remark #4
The output was sometimes 49 and sometimes 50. The matrices were printed like:
1 2 3 4
5 6 7 8
9 A B C
D E 0 F
1 2 3 4
5 6 7 8
9 A B C
D E F 0
... 50 times