You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从边文本文件构建图时遇Java NoSuchElementException运行时错误

问题分析与修复:Java图构建中的NoSuchElementException

错误根源定位

先看报错指向的代码行(Graph.java:64):

adjLists[v] = new Vertex(fileScanner.next(), null);

这个异常的核心原因是你的Scanner已经被读空了:

  • 你第一次用while (fileScanner.hasNext())循环收集唯一节点时,已经把整个文件的内容读完,Scanner的指针移到了文件末尾。
  • 紧接着你直接用同一个Scanner去读取顶点和边,此时fileScanner.next()找不到任何可读取的内容,就抛出了NoSuchElementException。

另外你的顶点读取逻辑也有问题:第二次读取时,文件里存的是成对的边节点,不是单独的顶点列表,直接调用fileScanner.next()完全不符合文件内容结构。

两种可行修复方案

方案1:重新打开文件读取内容

收集完唯一节点后,关闭当前Scanner,重新创建一个新的Scanner从头读取文件,这样就能正常处理边的内容了:

public Graph(String file) throws FileNotFoundException {
    // 第一步:收集所有唯一节点
    Scanner fileScanner = new Scanner(new File(file));
    fileScanner.useDelimiter("[^A-Za-z0-9]");
    ArrayList<String> uniqueWords = new ArrayList<>();
    while (fileScanner.hasNext()) {
        String nextWord = fileScanner.next();
        if (!uniqueWords.contains(nextWord)) {
            uniqueWords.add(nextWord);
        }
    }
    fileScanner.close(); // 关闭已读空的Scanner

    adjLists = new Vertex[uniqueWords.size()];
    // 用收集到的唯一节点初始化顶点数组
    for (int v = 0; v < adjLists.length; v++) {
        adjLists[v] = new Vertex(uniqueWords.get(v), null);
    }

    // 第二步:重新打开文件读取边
    fileScanner = new Scanner(new File(file));
    fileScanner.useDelimiter("[^A-Za-z0-9]");

    // 读取成对的边节点
    while (fileScanner.hasNext()) {
        if (!fileScanner.hasNext()) break; // 防止文件末尾出现单个无效节点
        String name1 = fileScanner.next();
        if (!fileScanner.hasNext()) break;
        String name2 = fileScanner.next();
        
        int v1 = indexForName(name1);
        int v2 = indexForName(name2);
        
        // 双向添加邻接关系
        adjLists[v1].adjList = new Neighbor(v2, adjLists[v1].adjList);
        adjLists[v2].adjList = new Neighbor(v1, adjLists[v2].adjList);
    }
    fileScanner.close();
}

方案2:一次性读取所有内容到内存

把文件所有内容先存入List,这样可以多次遍历,不需要重复打开文件,效率更高:

public Graph(String file) throws FileNotFoundException {
    Scanner fileScanner = new Scanner(new File(file));
    fileScanner.useDelimiter("[^A-Za-z0-9]");
    List<String> allWords = new ArrayList<>();
    Set<String> uniqueSet = new HashSet<>(); // 用HashSet提升去重效率

    // 读取所有单词,同时收集唯一节点
    while (fileScanner.hasNext()) {
        String word = fileScanner.next();
        allWords.add(word);
        uniqueSet.add(word);
    }
    fileScanner.close();

    ArrayList<String> uniqueWords = new ArrayList<>(uniqueSet);
    adjLists = new Vertex[uniqueWords.size()];
    
    // 初始化顶点,同时构建名称到索引的映射(优化后续查询)
    Map<String, Integer> nameToIndex = new HashMap<>();
    for (int v = 0; v < adjLists.length; v++) {
        String name = uniqueWords.get(v);
        adjLists[v] = new Vertex(name, null);
        nameToIndex.put(name, v);
    }

    // 遍历allWords处理边(每次取两个单词为一组)
    for (int i = 0; i < allWords.size(); i += 2) {
        if (i + 1 >= allWords.size()) break; // 避免越界
        String name1 = allWords.get(i);
        String name2 = allWords.get(i + 1);
        
        int v1 = nameToIndex.get(name1);
        int v2 = nameToIndex.get(name2);
        
        adjLists[v1].adjList = new Neighbor(v2, adjLists[v1].adjList);
        adjLists[v2].adjList = new Neighbor(v1, adjLists[v2].adjList);
    }
}

// 优化后的indexForName方法
int indexForName(String name) {
    // 这里可以直接用上面的nameToIndex映射,或者保持原逻辑,建议替换成map查询
    return nameToIndex.getOrDefault(name, -1);
}

额外优化提示

  • 用HashSet替代ArrayList做去重,把去重的时间复杂度从O(n)降到O(1)。
  • 提前构建nameToIndex的HashMap,查询节点索引的效率也会从O(n)优化到O(1),适合节点数量较多的场景。

内容的提问来源于stack exchange,提问作者Teju_M

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 03:51:13