假设一行输入适合 JVM 堆,在 Java 中从输入中解析字符串的三种常用方法是:
-
java.util.Scanner
-
java.io.BufferedReader#readLine & java.util.StringTokenizer
-
java.io.BufferedReader#readLine & java.lang.String#split
我不清楚哪种方法最适合这个问题,所以我决定尝试一下。我生成了测试数据,为每种方法实现了一个解析器,并对结果进行计时。
测试数据
我生成了4个测试数据文件:
- testdata_1k.txt - 大小 20KB
- testdata_10k.txt - 大小 205KB
- testdata_100k.txt - 大小 2MB
- testdata_1000k.txt - 大小 20M
我生成的文件与您描述的格式相匹配。每个, 分隔元素都是一个随机整数。如果:,文件名中的数字描述了每边的元素数量。例如,testdata_1k.txt 左侧有 1000 个元素,右侧有 1000 个元素。
测试代码
这是我用来测试每种方法的代码。请注意,这些不是生产质量代码的示例。
扫描码
public Map<String, Boolean> scanner(InputStream stream) {
final Scanner in = new Scanner(new BufferedInputStream(stream));
final HashMap<String, Boolean> result = new HashMap<String, Boolean>();
final HashSet<String> left = new HashSet<String>();
in.useDelimiter(",");
boolean leftSide = true;
while (in.hasNext()) {
String token = in.next();
if (leftSide) {
int delim = token.indexOf(':');
if (delim >= 0) {
left.add(token.substring(0, delim));
String rightToken = token.substring(delim + 1, token.length());
result.put(rightToken, left.contains(rightToken));
leftSide = false;
} else {
left.add(token);
}
} else {
result.put(token, left.contains(token));
}
}
return result;
}
StringTokenizer 代码
public Map<String, Boolean> stringTokenizer(InputStream stream) throws IOException {
final BufferedReader in = new BufferedReader(new InputStreamReader(stream));
final HashMap<String, Boolean> result = new HashMap<String, Boolean>();
final StringTokenizer lineTokens = new StringTokenizer(in.readLine(), ":");
final HashSet<String> left = new HashSet<String>();
if (lineTokens.hasMoreTokens()) {
final StringTokenizer leftTokens = new StringTokenizer(lineTokens.nextToken(), ",");
while (leftTokens.hasMoreTokens()) {
left.add(leftTokens.nextToken());
}
}
if (lineTokens.hasMoreTokens()) {
final StringTokenizer rightTokens = new StringTokenizer(lineTokens.nextToken(), ",");
while (rightTokens.hasMoreTokens()) {
String token = rightTokens.nextToken();
result.put(token, left.contains(token));
}
}
return result;
}
String.split 代码
public Map<String, Boolean> split(InputStream stream) throws IOException {
final BufferedReader in = new BufferedReader(new InputStreamReader(stream));
final HashMap<String, Boolean> result = new HashMap<String, Boolean>();
final String[] splitLine = in.readLine().split(":");
final HashSet<String> left = new HashSet<String>(Arrays.asList(splitLine[0].split(",")));
for (String element : splitLine[1].split(",")) {
result.put(element, left.contains(element));
}
return result;
}
时间
我对每个文件运行每种方法 6 次。我把第一个样品扔了出去。以下代表剩余 5 个样本的平均值。
扫描仪
- testdata_1k.txt - 23.2948 毫秒
- testdata_10k.txt - 39.5036 毫秒
- testdata_100k.txt - 240.5626 毫秒
- testdata_1000k.txt - 2671.5132 毫秒
字符串标记器
- testdata_1k.txt - 31.2344 毫秒
- testdata_10k.txt -14.7926 毫秒
- testdata_100k.txt - 102.6412 毫秒
- testdata_1000k.txt - 1353.073 毫秒
字符串.split
- testdata_1k.txt - 8.9596 毫秒
- testdata_10k.txt - 7.8396 毫秒
- testdata_100k.txt - 63.4854 毫秒
- testdata_1000k.txt - 947.8384 毫秒
结论
假设您的数据适合 JVM 堆,与 StringTokenizer 和 Scanner 相比,String.split 的解析速度很难超越。