1. 概述
在Java中,我们通常会从一个Scanner
对象读取输入。此外,这些输入常常会被存储在一个数组中,以便进行进一步处理。
在这篇教程中,我们将探讨如何在Java中将Scanner
的输入保存到数组中。
2. 问题介绍
当我们把Scanner
的输入作为数组存储时,可能有三种情况:
- 单行输入 - 每个令牌是一个数组元素。
- 多行输入 - 每行是一个数组元素。
- 多行输入 - 每行的每个令牌是一个数组元素。
本教程将讨论这些情况。为了简化,我们将通过字符串喂给Scanner
对象,并使用单元测试断言来验证是否得到预期的结果。
3. 单行输入:每个令牌一个数组元素
首先,假设Scanner
对象有以下输入:
String input = "Java Kotlin Ruby Python Go\n";
如图所示,输入行有五个令牌。因此,结果数组应该看起来像这样:
String[] expected = new String[] { "Java", "Kotlin", "Ruby", "Python", "Go" };
Java Scanner
的默认分隔符是空格。而且,Scanner.next()
方法允许我们从输入中读取下一个令牌。因此,我们可以使用循环调用next()
方法来填充数组:
Scanner scanner1 = new Scanner(input);
String[] result1 = new String[5];
int i = 0;
while (i < result1.length) {
result1[i] = scanner1.next();
i++;
}
assertArrayEquals(expected, result1);
细心的读者可能会注意到,这个解决方案只适用于行中有五个令牌的情况,因为我们定义了数组的容量(5)。这是因为Java数组是固定的大小,我们在创建它时需要设置其大小。
显然,当不知道输入中有多少元素时,这种方法不太方便。
另一种选择是,我们可以使用Scanner.nextLine()
方法读取整个输入行,并使用split()
方法将行分割成数组:
Scanner scanner2 = new Scanner(input);
String[] result2 = scanner2.nextLine().split("\\s+");
assertArrayEquals(expected, result2);
4. 多行输入:每行一个数组元素
接下来,构建多行输入:
String input = new StringBuilder().append("Baeldung Java\n")
.append("Baeldung Kotlin\n")
.append("Baeldung Linux\n")
.toString();
然后,我们期望输入中的每一行都成为结果数组的一个元素:
String[] expected = new String[] {"Baeldung Java", "Baeldung Kotlin", "Baeldung Linux"};
我们可以使用Scanner.nextLine()
读取输入行,并在循环中填充数组:
String[] result = new String[3];
Scanner scanner = new Scanner(input);
int i = 0;
while (i < result.length) {
result[i] = scanner.nextLine();
i++;
}
assertArrayEquals(expected, result);
同样,我们在代码中声明数组对象时设置了大小(3)。
由于List
具有动态大小,如果我们不知道输入中有多少行,我们可以使用List
来存储行:
List<String> expected = Lists.newArrayList("Baeldung Java", "Baeldung Kotlin", "Baeldung Linux");
List<String> result = new ArrayList<>();
Scanner scanner = new Scanner(input);
while (scanner.hasNextLine()) {
result.add(scanner.nextLine());
}
assertEquals(expected, result);
当然,如果最终结果必须是数组,我们可以轻松地将结果List
转换为数组。
5. 多行输入:每个令牌一个数组元素
现在,让我们看看如何将多行输入的令牌存储在数组中。假设我们有以下两行输入:
String input = new StringBuilder().append("Linux Windows MacOS\n")
.append("Java Kotlin Python Go\n")
.toString();
并且我们希望有一个包含这些元素的数组:
String[] expected = new String[] { "Linux", "Windows", "MacOS", "Java", "Kotlin", "Python", "Go" };
为了解决这个问题,我们可以使用Scanner.nextLine()
读取每一行,并对每一行进行split()
以将其转换为数组。同时,我们需要将这个数组合并到最终的结果数组中。
接下来,我们来看看它是如何工作的:
String[] result = new String[] {};
Scanner scanner = new Scanner(input);
while (scanner.hasNextLine()) {
String[] lineInArray = scanner.nextLine().split("\\s+");
result = ObjectArrays.concat(result, lineInArray, String.class);
}
assertArrayEquals(expected, result);
在上面的测试中,我们使用了Guava的ObjectArrays.concat()
方法来连接两个数组,以简化操作。
然而,连接数组会产生一个新的数组,并将两个数组复制到新数组中。因此,这种做法在大量输入行的情况下可能会很慢。
为了获得更好的性能,我们可以使用List
来存储令牌:
List<String> expected = Lists.newArrayList("Linux", "Windows", "MacOS", "Java", "Kotlin", "Python", "Go");
List<String> result = new ArrayList<>();
Scanner scanner = new Scanner(input);
while (scanner.hasNext()) {
result.add(scanner.next());
}
assertEquals(expected, result);
再次强调,如果需要,我们可以将列表结果转换为数组。
6. 数组与List
的比较
在解决我们的问题的过程中,我们可能已经意识到将输入存储到List
中比数组更灵活。除了动态大小属性外,List
相对于数组还有其他优势,例如:
- 添加和删除元素更加方便
- 类型安全
- 提供方便的内置操作,如
indexOf()
,contains()
, 等等 - 有多种实现,如
ArrayList
、LinkedList
, 等等
因此,在Java项目中,我们应该考虑使用List
而不是数组。
7. 总结
在这篇文章中,我们学习了如何将Scanner
的输入保存到数组中。
我们还讨论了三种不同的情况,并通过示例探索了每种解决方案。
如往常一样,这里展示的所有代码片段都在GitHub上可用。