&引用;“优化”;在Android中访问游标:位置与列名

&引用;“优化”;在Android中访问游标:位置与列名,android,cursor,Android,Cursor,从性能的角度来看:如果在每次访问我的游标时我都使用以下内容,是否可以: public static final String COLUMN_NAME = "my_column_name"; cursor.getString(cursor.getColumnIndex(COLUMN_NAME)); 或者,如果我改用此选项,我会看到绩效的显著改善: public static final int COLUMN_POSITION = #column_position; cursor.getStrin

从性能的角度来看:如果在每次访问我的游标时我都使用以下内容,是否可以:

public static final String COLUMN_NAME = "my_column_name";
cursor.getString(cursor.getColumnIndex(COLUMN_NAME));
或者,如果我改用此选项,我会看到绩效的显著改善:

public static final int COLUMN_POSITION = #column_position;
cursor.getString(COLUMN_POSITION);

我更喜欢第一种方法,因为代码的其余部分不取决于列在查询中的位置,而只取决于列的名称。所以我的问题是:是否值得牺牲这一点来换取使用恒定位置访问光标的“性能改进”?。您在Android应用程序中更喜欢哪种方法?

我想这与Android无关。在SQLite中,通过索引(列位置)访问列应该更快。

为了回答您的问题(顺便提一下,我的问题),我做了一些测试

此测试基本上是为了检查这两种情况下查询所用的时间:

  • 使用
    cursor.getString(cursor.getColumnIndex(COLUMN_NAME))
    方法
  • 首先获取列id,然后直接调用
    cursor.getString(column\u POSITION)
    方法
  • 为了使性能测试有意义,我在数据库中插入了5000行,然后我在这些元素上抛出了我的
    ContentProvider

    结果:

     ___________________________________________________________________________
    | Column count| Time (ms) getColumnIndex | Time (ms) columnId | improvement |
    |_____________|__________________________|____________________|_____________|
    | 500         | 34564                    | 30401              | 13%         |
    | 200         |  9987                    |  8502              | 17%         |
    | 100         |  4713                    |  4004              | 17%         |
    | 50          |  2400                    |  1971              | 21%         |
    | 20          |  1088                    |   915              | 19%         |
    |___________________________________________________________________________|
    
    因此,首先获取列id并直接调用
    getString()
    方法将花费大约20%更少的时间


    试验方法详情: 平台:安卓4.3上的Nexus7(2012)

    数据库创建:

    public static int TESTSPEEDCOLUMNCOUNT = 200;
    StringBuilder sb = new StringBuilder();
    sb.append("CREATE TABLE " + Tables.TESTSPEED + " (");
    sb.append(BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, ");
    for (int i = 0; i < (TESTSPEEDCOLUMNCOUNT - 1); ++i) {
        sb.append("C" + i + " TEXT, ");
    }
    sb.append("C" + (TESTSPEEDCOLUMNCOUNT - 1) + " TEXT)");
    db.execSQL(sb.toString());
    

    我知道,我的问题是:“显著”更快?我不知道确切的数字,但我认为只有在一个表中有100k+列时,差异才会显著。确定这一点的最佳方法是创建一个示例项目,并使用
    System.nanoTime()
    计算时间差。这确实适用于Android。通过ContentProvider返回的游标不需要SQLite支持。事实上,很多人不是。您可以查看游标的实现,自己来了解。在AbstractCursor.getColumnIndex的快速查看中,如果您有多个列(查找为O(n),其中n是列数),那么您绝对希望自己缓存索引值。在任何一种情况下,您都要调用getString(index)。这里更大的收获是,500列上的getColumnIndex本身需要将近4秒的时间。考虑到getColumnIndex代码的外观,缓存它很容易,也很值得。这个答案现在有点过时了。如果查看
    SQLiteCursor.java
    getColumnIndex(String columnName)
    的源代码,可以看到它们在方法顶部创建了一个字符串(name)到整数(index)的映射。因此,现在每个列、每个表只进行一次列名查找。
    public class ProviderTestSpeed extends ProviderTestCase2<MyProvider> {
    
        private ContentValues createElementForId(String id) {
            ContentValues cv = new ContentValues();
            for (int i = 0; i < TESTSPEEDCOLUMNCOUNT; ++i) {
                cv.put("C" + i, id);
            }
            return cv;
        }
    
    
    
        public void testSpeed() {
            Log.d(TAG, "testSpeed start columnCount = " + columnCount);
            ArrayList<ContentValues> list = new ArrayList<ContentValues>();
            ContentValues[] tabcv = {};
            for (int j = 0; j < 10; ++j) {
                list.clear();
                for (int i = 0; i < 500; ++i) {
                    ContentValues cv = createElementForId(String.valueOf(i));
                    list.add(cv);
                }
                mContentResolver.bulkInsert(TestSpeedCONTENT_URI, list.toArray(tabcv));
            }
            Log.d(TAG, "testSpeed insertFinished");
            Cursor cursor = mContentResolver.query(TestSpeedCONTENT_URI, null, null, null, null);
            cursor.moveToFirst();
            Log.d(TAG, "testSpeed itemCount = " + cursor.getCount() + " columnCount=" + cursor.getColumnCount());
    
            // build the tab to avoid dynamic allocation during the mesure
            ArrayList<String> listColumns = new ArrayList<String>();
            for (int i = 0; i < TESTSPEEDCOLUMNCOUNT; ++i) {
                listColumns.add("C" + i);
            }
            String[] tabColumnsType = {};
            String[] tabColumns = listColumns.toArray(tabColumnsType);
    
            Date now = new Date();
            long start = now.getTime();
            do {
                for (int i = 0; i < TESTSPEEDCOLUMNCOUNT; ++i) {
                    // get the all the columns of the table
                    cursor.getString(cursor.getColumnIndex(tabColumns[i]));
                }
            } while (cursor.moveToNext());
            now = new Date();
            long end = now.getTime();
    
            Log.d(TAG, "testSpeed took " + (end - start) + " with getColumnIndex at each time");
            cursor.moveToFirst();
            now = new Date();
            start = now.getTime();
            do {
                for (int i = 0; i < TESTSPEEDCOLUMNCOUNT; ++i) {
                    // get the all the columns of the table using directly the column id
                    cursor.getString(i);
                }
            } while (cursor.moveToNext());
            now = new Date();
            end = now.getTime();
            Log.d(TAG, "testSpeed took " + (end - start) + " with getColumnIndex before loop");
        }
    }
    
    W/CursorWindow(1628): Window is full: requested allocation 2412 bytes, free space 988 bytes, window size 2097152 bytes