【发布时间】:2014-08-20 00:04:31
【问题描述】:
我目前正在开发各种统计数据的应用程序。 一项任务是分析大量句子的字数。
规格为:
- 从 SQLiteDatabase 读取句子(最多 20k,平均约 15 个单词)
- 转换:用空格分割(得到句子的单词)
- 转换:toLowerCase(尽量减少单词的变化)
- 转换:替换 [^a-zA-Z](原因同上)
- 获取前 x 个(尚不确定,可能是 10-15 个)最常用单词的单词 + 计数
- 如果消息已发送/接收,则保留一个标志
这是我目前的做法:
db.execSQL("create temp table if not exists WORDS (WORD varchar, SENT integer)");
Cursor c1 = db.rawQuery("select lower(MSG) as SENTENCE, SENT from MESSAGELIST",null);
while (c.moveToNext()) {
String[] words = c.getString(c.getColumnIndex("SENTENCE")).split(
"\\s+");
int from_me = c.getInt(c.getColumnIndex("SENT"));
for (int i = 0; i < words.length; i++) {
words[i] = words[i].replaceAll("[^a-zA-z]", "");
if (!words[i].equals("")) {
db.execSQL("insert into WORDS values ('" + words[i] + "', "
+ from_me + ")");
}
}
}
Cursor c2 = db.rawQuery(
"select WORD, COUNT(*) as CNT from WORDS where SENT=0 group by WORD order by CNT desc limit 10",
null);
Cursor c3 = db.rawQuery(
"select WORD, COUNT(*) as CNT from WORDS where SENT=1 group by WORD order by CNT desc limit 10",
null);
我已经假设这段代码很慢。我猜字符串操作需要很多时间。
仅仅为了查询而从数据库中提取并重新输入也感觉不对。但是,我知道PostgreSQL 中有regexp_split_to_array 和regexp_split_to_table,这将使查询可以留在数据库中。我还没有在SQLite找到解决方案来做到这一点
我花了很多时间试图找出不同的解决方案,但我现在有点卡住了。是否有任何(相对)快速的方法来执行所需的任务?我也愿意接受建议,以使字数尽可能合理。
带有一些建议实现的当前版本:
改进:
- Prepared Statements:快约 29%
- 预编译正则表达式:快约 21%
- 评论部分表明我的计数实现,但这种方法提高了运行时间(有和没有索引)
- 通过事务批量插入:大约快 9%
- 用于替换的 CharMatcher:快约 8%
-
用于计数的 HashMultiset:快约 2%
c = db.rawQuery("select lower(DATA) as SENTENCE, SENT from MESSAGELIST", null); CharMatcher pat_rep = CharMatcher.inRange('A', 'Z') .or(CharMatcher.inRange('a', 'z')).precomputed(); Pattern pat_split = Pattern.compile("\\s"); HashMultiset<String> sent = HashMultiset.create(); HashMultiset<String> rcvd = HashMultiset.create(); while (c.moveToNext()) { String[] words = pat_split.split(c.getString(c.getColumnIndex("SENTENCE"))); int from_me = c.getInt(c.getColumnIndex("SENT")); for (int i = 0; i < words.length; i++) { words[i] = pat_rep.retainFrom(words[i]); if (!words[i].equals("")) { if (from_me == 1) { sent.add(words[i]); } else { rcvd.add(words[i]); } } } } db.execSQL("create temp table if not exists WORDS (WORD varchar, SENT integer, CNT integer)"); SQLiteStatement ins = db.compileStatement("insert into WORDS values (?, ?, ?)"); db.beginTransaction(); Iterator<String> i = sent.iterator(); while (i.hasNext()) { String in = i.next(); ins.bindString(1, in); ins.bindLong(2, 1); ins.bindLong(3, sent.count(in)); ins.executeInsert(); ins.clearBindings(); } i = rcvd.iterator(); while (i.hasNext()) { String in = i.next(); ins.bindString(1, in); ins.bindLong(2, 0); ins.bindLong(3, rcvd.count(in)); ins.executeInsert(); ins.clearBindings(); } db.setTransactionSuccessful(); db.endTransaction(); c = db.rawQuery( "select WORD, CNT from WORDS where SENT=0 group by WORD order by CNT desc limit 10", null); Cursor c2 = db.rawQuery( "select WORD, CNT from WORDS where SENT=1 group by WORD order by CNT desc limit 10", null);
【问题讨论】:
-
如果要进行复杂的统计计算,为什么这个数据库只存在于应用程序上?你能不把它发送到你的服务器吗?
-
@Falmarri:我希望应用程序脱机运行,因为没有服务器可以将其发送到(截至目前);)另外:除了使用服务器之外还有什么好处'表现?
-
您必须在您的
msg和word列上放置一些索引,尤其是当您的数据库中有很多记录时。
标签: java android performance sqlite dictionary