题目:1000万字符串,其中有些是重复的,需要把重复的全部去掉,保留没有重复的字符串。请怎么设计和实现?
大数据的字符串处理我一般想到了trie树和hashmap,jdk里有hashmap的实现,所以想先用hashmap来试试效果,在用hashmap来测试前先编个小代码,用来生成1000万的字符串,使用随机函数来选择字符:
//生成sum个单词,并输入到word.txt文件中去。
public static void produceWord(int sum){
char[] c = {'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o'
,'p','q','r','s','t','u','v','w','x','y','z'};
Random r = new Random();
int length =0;
int count =0;
StringBuffer sb = new StringBuffer("");
while(count
我用上面的函数生成100万个单词并写入到文件中花了670秒,也就是10分钟,因为每个单词的最大长度是10,while循环最坏会有1000万次的执行(1000万的字符串太久了,所以不想试了),生成的word.txt文件中一行对应一个单词,下面来用jdk自带的hashmap来排除100万个单词里面的重复单词,即题目的要求:
public static void main(String[] args) {
long time = 0;
time = TimeUtils.printTime();//这是我的工具类,直接输出当前的时间
// produceWord(1000000);
try {
BufferedReader br = new BufferedReader(new FileReader(FileUtils.getFile("word.txt")));
FileWriter fw = new FileWriter(FileUtils.getFile("treeWord.txt"),true);
HashMap hm = new HashMap();
String s = null;
while((s=br.readLine())!=null){
if(!hm.containsKey(s)){ //如果hm没有包含s这个单词,则把s加入到hm,同时写入文件treeWord.txt中
hm.put(s, 1);
//输出到文件
fw.write(s);
fw.write("\r\n");
fw.flush();
}
}
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
time = TimeUtils.printTime()-time;
TimeUtils.takeTime(time);//总共花费了多少秒
}
输出:
1333521185421
1333521201546
花费时间:16.125秒
接下来想试试用trie树来解决这个问题,jdk没有trie树,只好自己写一个了,先简单的介绍一下trie树,原理很简单,比如要表示i am a coder 这个句子的trie树,画个大致图就明白了:
当然,trie有很多变种,节点存储的东西也不一定一样,我这里节点存储了字符和一个数字,数字为0表示当前所在的节点没有单词,为1表示当前节点有单词,应该很好理解。
好了,接下来开始构建trie树,
static class treeNode {
treeNode[] node = null;
char value;
int count;// 单词计数,如果为0表示以此节点结尾的字符串,大于0则表示有count个以它结尾的字符串
int size;// 有几个孩子
................................
// 循环添加字符串s到当前节点中,前提是已经判断了当前节点不存在以s开头的字符
public void addString(String s) {
char[] c = s.toCharArray();
treeNode tn = this;
int i = 0;
while (i < c.length) {
tn.addChild(new treeNode(c[i]));
tn = tn.node[tn.contain(c[i])];
i++;
}
tn.count++; //当前节点的字符串树加一
}
// 添加一个孩子,前提是已经判断了当前节点不存在这个子节点
public void addChild(treeNode child) {
if (getSize() == node.length) {
treeNode[] temp = new treeNode[node.length * 2];
System.arraycopy(node, 0, temp, 0, node.length);
node = temp;
}
node[size] = child;
size++;
}
// 如果有包含c的子节点则返回节点的位置
public int contain(char c) {
treeNode tn = new treeNode(c);
for (int i = 0; i < size; i++) {
if (node[i].isEqual(tn)) {
return i;
}
}
return -1;
}
//判断一个字典树中是否存在字符串str,只有返回值大于0才表示有这个字符串
private int contain(String str) {
if(str==null || str == "")
return -1;
treeNode nd = root;
int i =0;
int place;
while(i //续向下查找
tn = tn.node[count];
i++;
if(i==str.length()){
tn.count++;//所在树本来已经包含这个字符串,所以count数加一
}
}
if (i != str.length()) { //在str[i]后面的没有添加进trie树中
String s = str.substring(i);
tn.addString(s); // 直接调用节点的方法addString,插入str[i]后面的字符串
}
}
public static void main(String[] args) {
long time = TimeUtils.printTime();
TrieTree t = new TrieTree(FileUtils.getFile("word.txt"));
time = TimeUtils.printTime()-time;
System.out.println("构建trie树花费了:");
TimeUtils.takeTime(time);
System.out.println("开始输出......");
time = TimeUtils.printTime();
t.printFile(FileUtils.getFile("treeWord.txt"));
time = TimeUtils.printTime()-time;
System.out.println("输出trie树到文件花费了...");
TimeUtils.takeTime(time);
}
1333521079359
1333521087187
构建trie树花费了:
花费时间:7.828秒
开始输出......
1333521087187
输出到treeWord.txt成功
1333521099843
输出trie树到文件花费了...
花费时间:12.656秒
加起来总共花费20秒的样子,跟上面haspmap差不多的速度,当然,我的机子运行起来比较卡,所以严格的来说,速度可能不是很准确,但大致还是知道了,hashmap的速度会要快一些,因为在添加一个字符串的时候,hashmap直接用哈希函数就能定位,然后选择是否put和写入文件,但是trie树需要在子节点中比较。
总结:1:这是根据“1000万字符串”而写的trie树,没有比较好的结构性,封装的不是很好。
2:个人觉得trie树对hashmap的优势是,在大量重复的单词中,trie树需要的内存会低一些,hashmap的优势是查找快一些。
欢迎拍砖!
以上只给了关键代码,需要完整代码留下邮箱.....