【问题标题】:Is there a reason .contains() would not work with scanner?.contains() 是否有理由不适用于扫描仪?
【发布时间】:2021-11-11 11:42:19
【问题描述】:

我正在研究一个线性搜索问题,它获取一个姓名文件并将其与姓名和号码的电话簿文件进行比较。我现在唯一的任务是查看电话簿文件中有多少个名字。在我的 main 方法中的 if 语句之前,一切都按预期工作,但是对于我的生活,我无法弄清楚我做错了什么。通过测试,我可以打印出两个文件中的所有行,所以我知道我正在正确读取文件。输出应为 500 / 500,因为所有名称都在超过一百万行的电话簿文件中。请帮忙。

package phonebook;

import java.util.Objects;
import java.util.Scanner;
import java.io.File;
import java.io.FileNotFoundException;

public class Main {
    final static String NAME_PATH = "C:\\Users\\{user}\\Downloads\\find.txt";
    final static String PHONEBOOK_PATH = "C:\\Users\\{user}\\Downloads\\directory.txt";

    private static String[] namesList(File file) {
        int count = 0;
        try (Scanner scanner = new Scanner(file)) {
            while (scanner.hasNextLine()) {
                scanner.nextLine();
                count++;
            }
            String[] names = new String[count];
            Scanner sc = new Scanner(file);
            for (int i = 0; i < count; i++) {
                names[i] = sc.nextLine();
            }
            return names;
        } catch (FileNotFoundException e) {
            System.out.printf("File not found: %s", NAME_PATH);
            return null;
        }
    }

    private static String timeDifference(long timeStart, long timeEnd) {
        long difference = timeEnd - timeStart;
        long minutes = (difference / 1000) / 60;
        long seconds = (difference / 1000) % 60;
        long milliseconds = difference - ((minutes * 60000) + (seconds * 1000));
        return "Time taken: " + minutes + " min. " + seconds + " sec. " +
                milliseconds + " ms.";
    }

    public static void main(String[] args) {
        File findFile = new File(NAME_PATH);
        File directoryFile = new File(PHONEBOOK_PATH);
        String[] names = namesList(findFile);
        int count = 0;
        try (Scanner scanner = new Scanner(directoryFile)) {
            System.out.println("Start searching...");
            long timeStart = System.currentTimeMillis();
            for (int i = 0; i < Objects.requireNonNull(names).length; i++) {
                while (scanner.hasNextLine()) {
                    if (scanner.nextLine().contains(names[i])) {
                        count++;
                        break;
                    }
                }
            }
            long timeEnd = System.currentTimeMillis();
            System.out.print("Found " + count + " / " + names.length + " entries. " +
                    timeDifference(timeStart, timeEnd));
        } catch (FileNotFoundException e) {
            System.out.printf("File not found: %s", PHONEBOOK_PATH);
        }
    }
}

输出:

Start searching...
Found 1 / 500 entries. Time taken: 0 min. 0 sec. 653 ms.
Process finished with exit code 0

【问题讨论】:

  • 为什么要打开文件两次?
  • 您显然在使用更新的 API,所以我会使用 Files.readAllLines 而不是扫描仪。它会给你一个字符串列表,它比 Scanner 更容易迭代。
  • 使用contains 的扫描仪循环使用hasNext,然后调用nextLinehasNext 返回 true 并不意味着一定有下一个,因此调用最终会永远阻塞

标签: java linear-search


【解决方案1】:

问题在于您的搜索方式。如果要迭代搜索,则需要为每个名称重新开始迭代。否则,您只是在电话簿中向前搜索。如果姓名列表中的第二个名字出现在第一个名字之前,那么您将只能找到一个名字,因为您在找到任何东西之前就已经用尽了电话簿。

但是,反复阅读电话簿文件是一项代价高昂的工作。相反,加载电话列表(就像您对姓名列表所做的那样),然后您可以迭代地搜索该列表以查找姓名列表中的每个元素。以下示例假设您使用的是列表而不是数组。使用 for-each 循环使发生的事情一目了然(相对于使用 Stream API)。

List<String> names = loadNames();
// each phonebook entry contains the name and the phone number in one string
List<String> phonebook = loadPhonebook();
int numFound = 0;

for (String name : names) {
  for (String entry : phonebook) {
    if (entry.contains(name)) {
      ++numFound;
    }
  }
}

然而,这仍然是一项昂贵的任务,因为您要反复进行嵌套迭代。根据电话簿文件的格式,您应该能够解析出名称并将它们存储在 TreeSet 中。那么搜索就是常数时间。

List<String> names = loadNames();
// phonebookNames are just the names - the phone number has been stripped away
TreeSet<String> phonebookNames = loadPhonebookNames();
int numFound = 0;

for (String name : names) {
  if (phonebookNames.contains(name)) {
    ++numFound;
  }
}

据推测,您的作业最终会想要将电话号码用于某事,因此您可能不想将其丢在地板上。您可以使用Map(键=名称,值=电话号码)捕获名称​​和电话号码,而不是只解析名称。那么你就可以这样计算名字的存在了。

List<String> names = loadNames();
// phonebook is a Map of phone number values keyed on name
Map<String,String> phonebook = loadPhonebook();
int numFound = 0;

for (String name : names) {
  if (phonebook.containsKey(name)) {
    ++numFound;
  }
}

【讨论】:

    【解决方案2】:

    您正在为每个名称(使用 nextLine)在文件中前进,您应该对每一行的名称进行循环。

    在您的代码中,如果您的名字 (name[0]) 在文件的最后一行,那么您在第一次迭代时已经位于文件的末尾,并且在搜索第二个名字时,有已经没有线路了。

    试试这样的:

    while (scanner.hasNextLine()) {
      String line = scanner.nextLine();
      for (int i = 0; i < Objects.requireNonNull(names).length; i++) {
        if (line.contains(names[i])) {
          count++;
          break;
        }
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2012-08-08
      • 2012-05-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-04
      • 1970-01-01
      • 2018-05-12
      相关资源
      最近更新 更多