.contains() 不适用于扫描仪是否有原因?

Is there a reason .contains() would not work with scanner?

我正在研究一个线性搜索问题,该问题获取姓名文件并将其与姓名和号码的电话簿文件进行比较。我现在唯一的任务是查看电话簿文件中有多少个名字。在我的主要方法中的 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

您正在为每个名称在文件中向前移动(使用 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;
    }
  }
}

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

但是,反复阅读 phone 图书文件是一项代价高昂的工作。相反,加载 phone 列表(就像您对名称列表所做的那样),然后您可以迭代地在该列表中搜索名称列表中的每个元素。以下示例假设您使用的是 List 而不是数组。使用 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;
    }
  }
}

但是,这仍然是一项昂贵的任务,因为您要反复进行嵌套迭代。根据 phonebook 文件的格式,您应该能够解析出名称并将它们存储在 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;
  }
}

据推测,您的作业最终会想要使用 phone 号码来做某事,所以您可能不想把它丢在地上。您可以使用 Map (key=name, value=phone数字)。那么你就可以这样统计名字的存在了。

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;
  }
}