Java中使用ArrayList绘制不重叠圆形时出现Stack Overflow Error
解决Stack Overflow错误与不重叠圆形生成问题
你遇到的Stack Overflow错误,核心原因是CheckDistance方法的无限递归调用——当随机生成的坐标一直和上一个圆形重叠时,递归会无限进行,直到栈内存耗尽。除此之外,你的距离检查逻辑也有问题:当前只和最后一个生成的圆形比较,还是会出现和更早生成的圆形重叠的情况,而且random.nextInt()可能生成负数,导致圆形跑到面板外面。
下面一步步帮你解决这些问题:
问题分析
- 递归无终止保障:
CheckDistance用递归重试生成坐标,但如果随机数一直命中重叠区域,递归永远不会停止,直接触发栈溢出。 - 检查逻辑不完整:只和最后一个生成的圆形比较,忽略了之前所有已存在的圆形,无法保证全局不重叠。
- 随机数范围错误:
random.nextInt()返回的是整个int范围的随机数,包括负数,生成的圆形可能出现在面板可视区域外。
解决方案
1. 把递归改成循环
用while循环代替递归,避免栈溢出问题,循环直到生成符合条件的坐标。
2. 完善距离检查逻辑
新生成的坐标需要和所有已添加到列表中的Country进行重叠检查,而不是只和最后一个。
3. 限制随机数范围
确保生成的坐标在面板可视区域内,同时预留圆形的大小和间距,避免超出边界。
修改后的完整代码
import javax.swing.*; import java.awt.*; import java.awt.geom.Ellipse2D; import java.util.ArrayList; import java.util.List; import java.util.Random; public class Algo { public static void main(String[] args) { new Algo(); } public Algo() { EventQueue.invokeLater(() -> { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { ex.printStackTrace(); } JFrame frame = new JFrame("Airport"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(new CountriesPane()); frame.setSize(700, 700); frame.setLocationRelativeTo(null); frame.setVisible(true); }); } public class CountriesPane extends JPanel { private final List<Country> countries = new ArrayList<>(100); private final Random random = new Random(); private static final int PANEL_WIDTH = 700; private static final int PANEL_HEIGHT = 700; public CountriesPane() { for (int index = 0; index < 50; index++) { countries.add(createNonOverlappingCountry()); } } private Country createNonOverlappingCountry() { Country newCountry; boolean isOverlapping; do { // 生成在面板内的随机坐标,预留圆形大小和间距 int x = random.nextInt(PANEL_WIDTH - Country.SIZE - Country.SPACE); int y = random.nextInt(PANEL_HEIGHT - Country.SIZE - Country.SPACE); newCountry = new Country(x, y); // 检查是否和已有的所有圆形重叠 isOverlapping = false; for (Country existing : countries) { if (isOverlapping(newCountry, existing)) { isOverlapping = true; break; } } } while (isOverlapping); return newCountry; } // 检查两个圆形是否重叠(包括边缘接触) private boolean isOverlapping(Country a, Country b) { double dx = a.getX() - b.getX(); double dy = a.getY() - b.getY(); // 两个圆形的中心距离 <= 半径之和 + 间距 double minDistance = Country.SIZE + Country.SPACE; return Math.sqrt(dx * dx + dy * dy) <= minDistance; } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g.create(); for (Country country : countries) { country.paint(g2d); } g2d.dispose(); } } public static class Country { public static final int SIZE = 20; public static final int SPACE = 10; private final Ellipse2D shape; public Country(int x, int y) { shape = new Ellipse2D.Double(x, y, SIZE, SIZE); } public double getX() { return shape.getX() + SIZE / 2.0; // 返回中心X坐标 } public double getY() { return shape.getY() + SIZE / 2.0; // 返回中心Y坐标 } public void paint(Graphics2D g2d) { g2d.setColor(Color.BLUE); g2d.fill(shape); } } }
关键修改点说明
- 用循环替代递归:
createNonOverlappingCountry方法用do-while循环生成坐标,直到找到不重叠的位置,彻底避免栈溢出。 - 全局重叠检查:每次生成新圆形时,遍历已有的所有圆形,用中心距离法判断是否重叠(更准确的圆形重叠判断方式)。
- 固定常量与封装:把圆形大小、间距定义为
Country类的静态常量,同时给Country添加获取中心坐标的方法,让逻辑更清晰。 - 边界控制:生成随机坐标时,减去圆形大小和间距,确保圆形完全在面板可视区域内。
- 代码规范:修正了变量命名(比如
Countries改成countries,符合Java驼峰命名规范),提升代码可读性。
这样修改后,不仅解决了Stack Overflow问题,还能真正生成50个不重叠的圆形,同时方便后续对每个Country对象进行操作。
内容的提问来源于stack exchange,提问作者TheGrimBoo
相关产品推荐
相关产品推荐

