Java 字符串拆分和比较-最快的方法
我有一个字符串,比如: 1,2,3:3,4,5 需要将分隔符左侧的字符串与分隔符右侧的字符串进行比较(Java 字符串拆分和比较-最快的方法,java,string,performance,split,Java,String,Performance,Split,我有一个字符串,比如: 1,2,3:3,4,5 需要将分隔符左侧的字符串与分隔符右侧的字符串进行比较(:)。现在,当我的意思是比较时,我实际上是想找出右部分(3,4,5)中的元素是否存在于左部分(1,2,3)中。右边的部分可以包含重复项,这很好(显然意味着我不能使用哈希集)。我已经完成了这项工作(细节如下),但我需要最快的方法来分割和比较上述字符串 这纯粹是一个基于性能的问题,旨在找出哪种方法更快,因为我将使用的实际输入是巨大的(在任何一边)。只有一行,将通过stdin读取 我是如何做到这一点的
:
)。现在,当我的意思是比较时,我实际上是想找出右部分(3,4,5)
中的元素是否存在于左部分(1,2,3)
中。右边的部分可以包含重复项,这很好(显然意味着我不能使用哈希集)。我已经完成了这项工作(细节如下),但我需要最快的方法来分割和比较上述字符串
这纯粹是一个基于性能的问题,旨在找出哪种方法更快,因为我将使用的实际输入是巨大的(在任何一边)。只有一行,将通过stdin读取
我是如何做到这一点的:
阅读标准文本
使用string.Split进行拆分,并将左侧部分存储在哈希集中
将正确的部分存储在阵列列表中
遍历数组列表,使用contains()
检查哈希集中是否存在元素
将输入读入字节[]
数组,将指针固定在代码的一侧
逐字节读取,同时计算整数元素:
int b = inputBytes[p++];
int d = b - '0';
if (0 <= d) {
if (d <= 9) {
element = element * 10 + d;
} else {
// b == ':'
}
} else {
// b == ','
// add element to the hash; element = 0;
...
}
if (p == inputBytesLength) {
inputBytesLength = in.read(inputBytes);
if (inputBytesLength == 0) { ... }
p = 0;
}
假设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个元素
测试代码
下面是我用来测试每种方法的代码。请注意,这些不是生产质量代码的示例
扫描代码
公共地图扫描仪(InputStream){
最终扫描仪输入=新扫描仪(新的BufferedInputStream(stream));
最终HashMap结果=新HashMap();
final HashSet left=新HashSet();
in.useDelimiter(“,”);
布尔值leftSide=true;
while(在.hasNext()中){
字符串标记=in.next();
如果(左侧){
int delim=token.indexOf(':');
如果(delim>=0){
左.add(token.substring(0,delim));
String rightToken=token.substring(delim+1,token.length());
put(rightToken,left.contains(rightToken));
leftSide=false;
}否则{
左。添加(令牌);
}
}否则{
put(token,left.contains(token));
}
}
返回结果;
}
字符串标记器代码
公共映射stringTokenizer(InputStream流)引发IOException{
final BufferedReader in=新的BufferedReader(新的InputStreamReader(流));
最终HashMap结果=新HashMap();
final StringTokenizer lineTokens=新的StringTokenizer(in.readLine(),“:”);
final HashSet left=新HashSet();
if(lineTokens.hasMoreTokens()){
final StringTokenizer leftTokens=新的StringTokenizer(lineTokens.nextToken(),“,”);
while(leftTokens.hasMoreTokens()){
添加(leftTokens.nextToken());
}
}
if(lineTokens.hasMoreTokens()){
final StringTokenizer rightTokens=新的StringTokenizer(lineTokens.nextToken(),“,”);
while(rightokens.hasMoreTokens()){
String token=rightTokens.nextToken();
put(token,left.contains(token));
}
}
返回结果;
}
String.split代码
公共映射拆分(InputStream流)引发IOException{
final BufferedReader in=新的BufferedReader(新的InputStreamReader(流));
最终HashMap结果=新HashMap();
最后一个字符串[]splitLine=in.readLine().split(“:”);
final HashSet left=新的HashSet(Arrays.asList(splitLine[0].split(“,”));
对于(字符串元素:拆分行[1]。拆分(“,”){
put(元素,左。包含(元素));
}
返回结果;
}
时机
我针对每个文件运行了每种方法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毫秒
String.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
的解析速度 关于重复,您在RHS上所说的HashSet
对我来说毫无意义。IIUYC重复项无论如何都会被忽略,因为你不知道发生了多少次。数字能有多大?好的,所以它没有意义的原因是因为我不能忽略
// as add()
int h = element * 0x9E3779B9;
int i = h >>> (32 - hashSizePower);
while (hash[i] != 0) {
if (--i < 0) i += hashSize;
}
hash[i] = element;
// contains() similarly
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;
}
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;
}
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;
}