基于多线程填充列表实现控制台计算器的数学运算问题求助
控制台计算器问题修复方案
核心问题分析
1. Calculate类运算逻辑错误
operation方法用嵌套循环导致每个第一个运算数重复和所有第二个运算数运算,最终只返回最后一次结果,不符合“对应位置元素运算”的需求- 仅实现加减运算,未处理乘除逻辑
- 未校验三个列表的长度一致性,存在索引越界风险
- 测试用例中三个列表长度不匹配(doubles有3个元素、doubles2有2个、symbols有4个),直接运行会抛出
IndexOutOfBoundsException
2. 多线程填充的潜在问题
- 虽用
Collections.synchronizedList保证了列表添加的线程安全,但填充完成后未校验三个列表长度一致性,会导致后续运算出错 - Test2类仅实现列表填充,未对接Calculate类的运算逻辑
3. 其他细节问题
- 除法运算未处理除零异常
operation方法参数中的result无意义,每次运算都会覆盖该值
修复后的代码实现
1. 修复后的Calculate类
import java.util.ArrayList; import java.util.List; public class Calculate { // 将字符串列表转换为Double列表,增加格式校验 public static List<Double> converter(List<String> list) throws NumberFormatException { List<Double> doubleList = new ArrayList<>(); for (String s : list) { doubleList.add(Double.parseDouble(s.trim())); } return doubleList; } public static Double addition(Double a, Double b) { return a + b; } public static Double subtraction(Double a, Double b) { return a - b; } public static Double multiplication(Double a, Double b) { return a * b; } // 除法运算处理除零异常 public static Double division(Double a, Double b) throws ArithmeticException { if (b == 0.0) { throw new ArithmeticException("除数不能为零"); } return a / b; } // 修正运算逻辑:对应位置元素运算,返回结果列表 public static List<Double> operation(List<Double> doubles1, List<Double> doubles2, List<String> symbols) { // 先校验三个列表长度一致 if (doubles1.size() != doubles2.size() || doubles1.size() != symbols.size()) { throw new IllegalArgumentException("运算数列表和符号列表长度必须一致"); } List<Double> results = new ArrayList<>(); for (int i = 0; i < doubles1.size(); i++) { Double a = doubles1.get(i); Double b = doubles2.get(i); String symbol = symbols.get(i); Double result; switch (symbol) { case "+": result = addition(a, b); break; case "-": result = subtraction(a, b); break; case "*": result = multiplication(a, b); break; case "/": result = division(a, b); break; default: throw new IllegalArgumentException("不支持的运算符号:" + symbol); } results.add(result); } return results; } // 测试方法 public static void main(String[] args) { List<Double> doubles1 = List.of(33.0, 50.0, 50.0); List<Double> doubles2 = List.of(17.0, 50.0, 25.0); List<String> symbols = List.of("+", "+", "/"); try { List<Double> results = operation(doubles1, doubles2, symbols); for (int i = 0; i < results.size(); i++) { System.out.printf("%f %s %f = %f%n", doubles1.get(i), symbols.get(i), doubles2.get(i), results.get(i)); } } catch (Exception e) { System.err.println("运算出错:" + e.getMessage()); } } }
2. 整合运算逻辑后的Test2类
import java.util.*; import java.util.concurrent.*; public class Test2 { private static final List<String> firstValues = Collections.synchronizedList(new ArrayList<>()); private static final List<String> symbols = Collections.synchronizedList(new ArrayList<>()); private static final List<String> secondValues = Collections.synchronizedList(new ArrayList<>()); private static final BlockingQueue<String> q1 = new LinkedBlockingQueue<>(); private static final BlockingQueue<String> qs = new LinkedBlockingQueue<>(); private static final BlockingQueue<String> q2 = new LinkedBlockingQueue<>(); private static volatile boolean running = true; public static void main(String[] args) throws InterruptedException { Scanner scanner = new Scanner(System.in); // 输入读取线程:将输入分发到对应队列 Thread reader = new Thread(() -> { while (running) { String input = scanner.nextLine().trim(); if (input.equals("=")) { running = false; // 发送停止信号 q1.offer("STOP"); qs.offer("STOP"); q2.offer("STOP"); break; } if (input.startsWith("1:")) { q1.offer(input.substring(2)); } else if (input.startsWith("s:")) { qs.offer(input.substring(2)); } else if (input.startsWith("2:")) { q2.offer(input.substring(2)); } else { System.out.println("无效输入,请使用格式:1:<数值>, s:<符号>, 2:<数值>,输入=结束"); } } }); // 处理队列并填充列表的工作线程 Thread worker1 = new Thread(() -> process(q1, firstValues, "第一个运算数")); Thread workerS = new Thread(() -> process(qs, symbols, "运算符号")); Thread worker2 = new Thread(() -> process(q2, secondValues, "第二个运算数")); reader.start(); worker1.start(); workerS.start(); worker2.start(); // 等待所有线程完成 reader.join(); worker1.join(); workerS.join(); worker2.join(); // 开始运算 try { List<Double> doubles1 = Calculate.converter(firstValues); List<Double> doubles2 = Calculate.converter(secondValues); List<Double> results = Calculate.operation(doubles1, doubles2, symbols); System.out.println("\n运算结果:"); for (int i = 0; i < results.size(); i++) { System.out.printf("%f %s %f = %f%n", doubles1.get(i), symbols.get(i), doubles2.get(i), results.get(i)); } } catch (NumberFormatException e) { System.err.println("数值格式错误:请输入有效的数字"); } catch (IllegalArgumentException | ArithmeticException e) { System.err.println("运算出错:" + e.getMessage()); } catch (Exception e) { System.err.println("未知错误:" + e.getMessage()); } } private static void process(BlockingQueue<String> queue, List<String> list, String name) { while (running || !queue.isEmpty()) { try { String value = queue.poll(100, TimeUnit.MILLISECONDS); if (value == null || value.equals("STOP")) { continue; } list.add(value); System.out.println("[" + name + "] 添加:" + value); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; } } } }
关键修复点说明
- 运算逻辑修正:将嵌套循环改为单循环,按索引对应取三个列表的元素,实现“对应位置运算”的需求,同时补充乘除运算的处理
- 异常处理:增加数值格式校验、除零异常捕获、列表长度一致性校验,避免运行时崩溃
- 多线程安全:保留
Collections.synchronizedList保证列表添加的线程安全,同时在运算前校验三个列表长度一致 - 功能整合:在Test2类的线程全部完成后,调用Calculate类的方法进行转换和运算,实现完整的控制台输入-填充-运算流程
内容的提问来源于stack exchange,提问作者Матвей Шавлов
相关产品推荐
相关产品推荐

