
Java如何计算算术表达式主要涉及到使用栈数据结构、解析表达式、使用现有库函数。其中,使用栈数据结构是计算算术表达式中最常用且高效的方法。接下来,我们将详细展开其中一种方法,并介绍其他的技巧和工具来实现这一目标。
一、使用栈数据结构
使用栈数据结构来计算算术表达式是一种经典的方法。栈是一种后进先出(LIFO)的数据结构,非常适合处理括号和操作符的优先级问题。我们可以使用两个栈,一个存放操作数,一个存放操作符。
1.1 中缀表达式转后缀表达式
中缀表达式是我们日常使用的表达式形式,比如 3 + 5 * 2。为了方便计算,我们需要将其转换为后缀表达式(逆波兰表示法),比如 3 5 2 * +。在转换过程中,我们利用栈来处理操作符的优先级。
import java.util.Stack;
public class InfixToPostfix {
public static int precedence(char ch) {
switch (ch) {
case '+':
case '-':
return 1;
case '*':
case '/':
return 2;
case '^':
return 3;
}
return -1;
}
public static String infixToPostfix(String expression) {
StringBuilder result = new StringBuilder();
Stack<Character> stack = new Stack<>();
for (int i = 0; i < expression.length(); ++i) {
char c = expression.charAt(i);
// If the character is an operand, add it to the output
if (Character.isLetterOrDigit(c))
result.append(c);
// If the character is '(', push it to the stack
else if (c == '(')
stack.push(c);
// If the character is ')', pop and output from the stack
// until an '(' is encountered
else if (c == ')') {
while (!stack.isEmpty() && stack.peek() != '(')
result.append(stack.pop());
stack.pop();
} else { // an operator is encountered
while (!stack.isEmpty() && precedence(c) <= precedence(stack.peek())) {
result.append(stack.pop());
}
stack.push(c);
}
}
// pop all the operators from the stack
while (!stack.isEmpty()) {
result.append(stack.pop());
}
return result.toString();
}
public static void main(String[] args) {
String expression = "3+5*2/(7-2)";
System.out.println(infixToPostfix(expression));
}
}
1.2 计算后缀表达式
一旦我们得到了后缀表达式,计算它就变得相对简单了。我们再次使用一个栈来存储操作数,遇到操作符时,弹出栈顶的两个操作数,进行相应的计算,并将结果压回栈中。
import java.util.Stack;
public class PostfixEvaluation {
public static int evaluatePostfix(String expression) {
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < expression.length(); i++) {
char c = expression.charAt(i);
if (Character.isDigit(c))
stack.push(c - '0');
else {
int val1 = stack.pop();
int val2 = stack.pop();
switch (c) {
case '+':
stack.push(val2 + val1);
break;
case '-':
stack.push(val2 - val1);
break;
case '*':
stack.push(val2 * val1);
break;
case '/':
stack.push(val2 / val1);
break;
}
}
}
return stack.pop();
}
public static void main(String[] args) {
String postfixExpression = "352*+72-/";
System.out.println(evaluatePostfix(postfixExpression));
}
}
二、使用现有库函数
Java中也有许多现成的库函数可以帮助我们计算算术表达式。常见的库有 JEXL、MVEL 等,这些库提供了丰富的功能,可以解析和计算复杂的表达式。
2.1 使用 JEXL 库
Apache Commons JEXL 是一个表达式语言库,支持动态脚本编写和解释。我们可以使用它来计算算术表达式。
首先,添加 JEXL 依赖:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-jexl3</artifactId>
<version>3.2</version>
</dependency>
然后,可以通过以下代码来计算表达式:
import org.apache.commons.jexl3.JexlBuilder;
import org.apache.commons.jexl3.JexlEngine;
import org.apache.commons.jexl3.JexlExpression;
import org.apache.commons.jexl3.MapContext;
public class JexlExample {
public static void main(String[] args) {
JexlEngine jexl = new JexlBuilder().create();
String expression = "3 + 5 * 2 / (7 - 2)";
JexlExpression e = jexl.createExpression(expression);
Object result = e.evaluate(new MapContext());
System.out.println(result);
}
}
2.2 使用 MVEL 库
MVEL 是一个功能强大的表达式语言库,它同样可以用于计算算术表达式。首先,添加 MVEL 依赖:
<dependency>
<groupId>org.mvel</groupId>
<artifactId>mvel2</artifactId>
<version>2.4.7.Final</version>
</dependency>
然后,可以通过以下代码来计算表达式:
import org.mvel2.MVEL;
public class MvelExample {
public static void main(String[] args) {
String expression = "3 + 5 * 2 / (7 - 2)";
Object result = MVEL.eval(expression);
System.out.println(result);
}
}
三、解析表达式
解析表达式是计算算术表达式的关键步骤,解析过程包括词法分析和语法分析。通过解析,我们可以将表达式分解成操作数和操作符,并按优先级进行计算。
3.1 词法分析
词法分析的目的是将表达式分解成一个个记号(token),每个记号可以是操作数、操作符或括号。下面是一个简单的词法分析器:
import java.util.ArrayList;
import java.util.List;
public class Lexer {
public static List<String> tokenize(String expression) {
List<String> tokens = new ArrayList<>();
int i = 0;
while (i < expression.length()) {
char c = expression.charAt(i);
if (Character.isDigit(c)) {
StringBuilder sb = new StringBuilder();
while (i < expression.length() && Character.isDigit(expression.charAt(i))) {
sb.append(expression.charAt(i++));
}
tokens.add(sb.toString());
} else if (c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')') {
tokens.add(Character.toString(c));
i++;
} else {
i++;
}
}
return tokens;
}
public static void main(String[] args) {
String expression = "3 + 5 * 2 / (7 - 2)";
List<String> tokens = tokenize(expression);
for (String token : tokens) {
System.out.println(token);
}
}
}
3.2 语法分析
语法分析的目的是根据记号生成一个语法树,然后根据语法树进行计算。下面是一个简单的语法分析器:
import java.util.List;
import java.util.Stack;
public class Parser {
public static int parse(List<String> tokens) {
Stack<Integer> values = new Stack<>();
Stack<Character> ops = new Stack<>();
for (String token : tokens) {
if (Character.isDigit(token.charAt(0))) {
values.push(Integer.parseInt(token));
} else if (token.charAt(0) == '(') {
ops.push('(');
} else if (token.charAt(0) == ')') {
while (ops.peek() != '(') {
values.push(applyOp(ops.pop(), values.pop(), values.pop()));
}
ops.pop();
} else if (token.charAt(0) == '+' || token.charAt(0) == '-' || token.charAt(0) == '*' || token.charAt(0) == '/') {
while (!ops.isEmpty() && hasPrecedence(token.charAt(0), ops.peek())) {
values.push(applyOp(ops.pop(), values.pop(), values.pop()));
}
ops.push(token.charAt(0));
}
}
while (!ops.isEmpty()) {
values.push(applyOp(ops.pop(), values.pop(), values.pop()));
}
return values.pop();
}
public static boolean hasPrecedence(char op1, char op2) {
if (op2 == '(' || op2 == ')')
return false;
if ((op1 == '*' || op1 == '/') && (op2 == '+' || op2 == '-'))
return false;
return true;
}
public static int applyOp(char op, int b, int a) {
switch (op) {
case '+':
return a + b;
case '-':
return a - b;
case '*':
return a * b;
case '/':
if (b == 0)
throw new UnsupportedOperationException("Cannot divide by zero");
return a / b;
}
return 0;
}
public static void main(String[] args) {
String expression = "3 + 5 * 2 / (7 - 2)";
List<String> tokens = Lexer.tokenize(expression);
System.out.println(parse(tokens));
}
}
四、扩展功能
除了基础的算术运算,我们还可以扩展更多的功能,比如支持变量、函数调用、数学库函数等。
4.1 支持变量
为了支持变量,我们需要在解析和计算过程中引入一个上下文(context),上下文存储了变量的值。
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
public class VariableSupport {
private static Map<String, Integer> context = new HashMap<>();
public static int parse(List<String> tokens) {
Stack<Integer> values = new Stack<>();
Stack<Character> ops = new Stack<>();
for (String token : tokens) {
if (Character.isDigit(token.charAt(0))) {
values.push(Integer.parseInt(token));
} else if (context.containsKey(token)) {
values.push(context.get(token));
} else if (token.charAt(0) == '(') {
ops.push('(');
} else if (token.charAt(0) == ')') {
while (ops.peek() != '(') {
values.push(applyOp(ops.pop(), values.pop(), values.pop()));
}
ops.pop();
} else if (token.charAt(0) == '+' || token.charAt(0) == '-' || token.charAt(0) == '*' || token.charAt(0) == '/') {
while (!ops.isEmpty() && hasPrecedence(token.charAt(0), ops.peek())) {
values.push(applyOp(ops.pop(), values.pop(), values.pop()));
}
ops.push(token.charAt(0));
}
}
while (!ops.isEmpty()) {
values.push(applyOp(ops.pop(), values.pop(), values.pop()));
}
return values.pop();
}
public static boolean hasPrecedence(char op1, char op2) {
if (op2 == '(' || op2 == ')')
return false;
if ((op1 == '*' || op1 == '/') && (op2 == '+' || op2 == '-'))
return false;
return true;
}
public static int applyOp(char op, int b, int a) {
switch (op) {
case '+':
return a + b;
case '-':
return a - b;
case '*':
return a * b;
case '/':
if (b == 0)
throw new UnsupportedOperationException("Cannot divide by zero");
return a / b;
}
return 0;
}
public static void main(String[] args) {
context.put("x", 5);
context.put("y", 2);
String expression = "3 + x * y / (7 - 2)";
List<String> tokens = Lexer.tokenize(expression);
System.out.println(parse(tokens));
}
}
4.2 支持函数调用
为了支持函数调用,我们需要在上下文中存储函数定义,并在解析和计算过程中进行相应的调用。
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import java.util.function.Function;
public class FunctionSupport {
private static Map<String, Integer> context = new HashMap<>();
private static Map<String, Function<Integer[], Integer>> functions = new HashMap<>();
public static int parse(List<String> tokens) {
Stack<Integer> values = new Stack<>();
Stack<Character> ops = new Stack<>();
for (String token : tokens) {
if (Character.isDigit(token.charAt(0))) {
values.push(Integer.parseInt(token));
} else if (context.containsKey(token)) {
values.push(context.get(token));
} else if (functions.containsKey(token)) {
// handle function call
} else if (token.charAt(0) == '(') {
ops.push('(');
} else if (token.charAt(0) == ')') {
while (ops.peek() != '(') {
values.push(applyOp(ops.pop(), values.pop(), values.pop()));
}
ops.pop();
} else if (token.charAt(0) == '+' || token.charAt(0) == '-' || token.charAt(0) == '*' || token.charAt(0) == '/') {
while (!ops.isEmpty() && hasPrecedence(token.charAt(0), ops.peek())) {
values.push(applyOp(ops.pop(), values.pop(), values.pop()));
}
ops.push(token.charAt(0));
}
}
while (!ops.isEmpty()) {
values.push(applyOp(ops.pop(), values.pop(), values.pop()));
}
return values.pop();
}
public static boolean hasPrecedence(char op1, char op2) {
if (op2 == '(' || op2 == ')')
return false;
if ((op1 == '*' || op1 == '/') && (op2 == '+' || op2 == '-'))
return false;
return true;
}
public static int applyOp(char op, int b, int a) {
switch (op) {
case '+':
return a + b;
case '-':
return a - b;
case '*':
return a * b;
case '/':
if (b == 0)
throw new UnsupportedOperationException("Cannot divide by zero");
return a / b;
}
return 0;
}
public static void main(String[] args) {
context.put("x", 5);
context.put("y", 2);
functions.put("max", args -> Math.max(args[0], args[1]));
String expression = "3 + max(x, y) * y / (7 - 2)";
List<String> tokens = Lexer.tokenize(expression);
System.out.println(parse(tokens));
}
}
总结
在本文中,我们探讨了在Java中计算算术表达式的多种方法,包括使用栈数据结构、使用现有库函数以及解析表达式。每种方法都有其优缺点,选择哪种方法取决于具体的需求和场景。
使用栈数据结构适合处理简单的算术表达式,能够清晰地处理操作符优先级和括号。使用现有库函数如JEXL和MVEL,可以快速实现复杂表达式的解析和计算,适合需要动态解析和计算表达式的场景。解析表达式的方法适合需要扩展功能的场景,如支持变量、函数调用等。通过这些方法,我们可以灵活地处理各种算术表达式的计算需求。
相关问答FAQs:
1. 如何在Java中计算算术表达式?
Java提供了一个强大的表达式求值引擎,可以用于计算算术表达式。您可以使用Java的内置库,例如ScriptEngine,来实现这一点。首先,您需要导入javax.script包,然后创建一个ScriptEngine对象。接下来,您可以使用ScriptEngine的eval()方法来计算算术表达式。例如:
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class ArithmeticExpressionCalculator {
public static void main(String[] args) {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
try {
String expression = "2 + 3 * 4";
Object result = engine.eval(expression);
System.out.println("计算结果: " + result);
} catch (ScriptException e) {
e.printStackTrace();
}
}
}
在上面的示例中,我们使用JavaScript引擎来计算算术表达式"2 + 3 * 4"。eval()方法返回一个Object类型的结果,您可以根据需要进行类型转换。
2. 如何处理带有变量的算术表达式?
如果您的算术表达式包含变量,您可以使用ScriptEngine的Bindings来传递变量值。首先,您需要创建一个Bindings对象,然后将变量名和值绑定到Bindings中。接下来,您可以使用ScriptEngine的setBindings()方法将Bindings对象传递给引擎。这样,您就可以在表达式中使用这些变量了。例如:
import javax.script.Bindings;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class VariableExpressionCalculator {
public static void main(String[] args) {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
try {
Bindings bindings = engine.createBindings();
bindings.put("x", 2);
bindings.put("y", 3);
String expression = "x + y * 4";
Object result = engine.eval(expression, bindings);
System.out.println("计算结果: " + result);
} catch (ScriptException e) {
e.printStackTrace();
}
}
}
在上面的示例中,我们创建了两个变量x和y,并将它们的值分别设置为2和3。然后,我们在表达式"x + y * 4"中使用了这些变量,并通过Bindings对象传递给ScriptEngine。
3. 如何处理复杂的算术表达式,例如带有括号和函数调用?
如果您的算术表达式比较复杂,例如包含括号和函数调用,您可以使用ScriptEngine的更多功能来处理。首先,您可以使用括号来明确指定运算的优先级。例如,"(2 + 3) * 4"将先计算括号内的表达式,然后再乘以4。其次,您可以使用JavaScript的内置数学函数,例如Math.sqrt()来计算平方根,Math.sin()来计算正弦值等等。例如:
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
public class ComplexExpressionCalculator {
public static void main(String[] args) {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("JavaScript");
try {
String expression = "(2 + 3) * Math.sqrt(16)";
Object result = engine.eval(expression);
System.out.println("计算结果: " + result);
} catch (ScriptException e) {
e.printStackTrace();
}
}
}
在上面的示例中,我们计算了表达式"(2 + 3) * Math.sqrt(16)",其中括号内的表达式先计算,然后再乘以16的平方根。您可以根据需要使用其他JavaScript内置函数来处理更复杂的表达式。
文章包含AI辅助创作,作者:Edit1,如若转载,请注明出处:https://docs.pingcode.com/baike/362268