.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
,然后调用nextLine
。 hasNext
返回 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 不适用于不同的视图