.contains() 是不是有理由不适用于扫描仪?

Posted

技术标签:

【中文标题】.contains() 是不是有理由不适用于扫描仪?【英文标题】: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 并不意味着一定有下一个,因此调用最终会永远阻塞 【参考方案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;
    
  

【讨论】:

以上是关于.contains() 是不是有理由不适用于扫描仪?的主要内容,如果未能解决你的问题,请参考以下文章

CGRect Contains Point 不适用于不同的视图

- 包含或 - 匹配多个值

Zxing 二维码扫描仪不适用于所有设备

var 不适用于 DataGridViewRow

条码扫描不适用于完整的 AVCaptureVideoPreviewLayer

条码扫描不适用于视觉 API