题目: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<sum){ length = r.nextInt(10)+1; for(int i=0;i<length;i++){ sb.append(c[r.nextInt(26)]); } FileUtils.writeWord(new String(sb),FileUtils.getFile("word.txt"));//这是我写的一个工具类,直接写入带刺到word.txt文件中 sb.delete(0, sb.length()); 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<String, Integer> hm = new HashMap<String, Integer>(); 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<str.length() && (place =nd.contain(str.charAt(i)))!=-1){
nd = nd.node[place];
i++;
}
if(i==str.length())
return nd.count;
else return -1;
}
// 在字典树中添加一个字符串 ,具体实现
private void addString(String str, treeNode node) {
treeNode tn = node;
int count;
int i = 0;
while (i < str.length()&& (count = tn.contain(str.charAt(i))) != -1 ) { //如果tn节点包含值为str[i]的子节点,则赋值这个子节点为新的tn,继 //续向下查找
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的优势是查找快一些。
欢迎拍砖!
以上只给了关键代码,需要完整代码留下邮箱.....