java - 创建一个快速的 Android 词典(字数统计)

标签 java android performance sqlite dictionary

我目前正在开发各种统计数据的应用程序。 其中一项任务是分析大量句子的字数。

规范为:

  • 从 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_arrayregexp_split_to_table ,这使得可以保留在数据库上进行查询。我还没有找到在 SQLite

中执行此操作的解决方案

我花了很多时间试图找出不同的解决方案,但现在有点陷入困境。是否有任何(相对)快速的方法来执行所需的任务?我也欢迎提出建议,使字数统计尽可能合理。

当前版本以及一些建议的实现:

改进:

  • 准备好的语句:速度提高约 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);
    

最佳答案

db.execSQL("insert into WORDS values ('" + words[i] + "', "
                    + from_me + ")");

数据库访问过多。为每个单词都敲击 DB 并不顺利。由于重复的单词较多,您可以将它们计入 Multiset 中。当内存紧张或完成时,将其与计数一起存储。

为每个事件创建单独的行也是没有意义的。添加一列count(最好以不同的方式调用它,因为“count”是一个关键字)。

使用准备好的语句。通过每次创建一个查询字符串,您可以强制数据库一次又一次地解析它。并且还为 GC 生成工作。

words[i] = words[i].replaceAll("[^a-zA-z]", "");

使用Pattern.compileCharMatcher 。后者在没有特殊字符的常见情况下不会产生垃圾。

private final CharMatcher alpha = CharMatcher.inRange('A', 'Z')
        .or(CharMatcher.inRange('a', 'z')).precomputed();

alpha.retainFrom(words[i]);

这应该有很大帮助,尤其是数据库方面的东西。尝试一下,如果还不够的话再来。

关于java - 创建一个快速的 Android 词典(字数统计),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25394873/

相关文章:

c# - 缓存对象的高效克隆

java - Eclipse模块问题jdk 10

performance - 将矩阵从 3d reshape 为 2d 保持行

java - 为什么这个打印只有 "false"而不是 "false false"?

android - 使用所需的时区从毫秒显示正确的时间

android:如何将图像添加到相册

android - Proguard 在优化代码时是否会去混淆字符串?

node.js - 在 memcached 持久连接或每个请求中

java - Tomcat 或 Java 中是否存在任何类似 beforeCrash() 或 beforeExit() 的函数

c# - 如何 IKVM Apache POI