Эффективный фильтр ArrayList в Java/Android

Я разрабатываю Android-приложение (Android 1.6), но это, вероятно, более общий вопрос Java.

У меня есть ArrayList около 10 000 объектов

объекты содержат 3 строки (firstName, middleName, lastName).

Пользователю предоставляется "окно поиска" на андроиде, где они могут искать определенный "объект", введя часть имени.

У меня есть класс (который я называю Filterer), который просматривает список из 10 000 для сопоставления объектов, а затем возвращает их как "подписок".

Поиск немного SLOW (особенно на Android-телефоне), и я уверен, что я не делаю поиск/фильтрацию наиболее эффективным способом.

Есть ли у кого-нибудь предложения по ускорению моего поиска? Мой код ниже. Одна из возможностей поиска по второстепенному "мастер-листу", который уже имеет каждую часть информации в нижнем регистре и конкатенирован... но могут быть дополнительные способы улучшить этот поиск, который также поможет.

ТИА!!

public void filterNames() {
  this.filteredList.clear();
  String sv = this.searchString.toString.trim().toLowerCase(); // search value
  for (int i = 0; i < this.masterList.size(); i++) {
    MyObject d = this.masterList.get(i);
    String fn = d.getFirstName().toString().toLowerCase();
    String mn = d.getMiddleName().toString().toLowerCase();
    String ln = d.getLastName().toString().toLowerCase();

    if (fn.indexOf(sv) >= 0 || 
        md.indexOf(sv) >= 0 || 
        ln.indexOf(sv) >= 0) {
      this.currentList.add(d);
    }
  }
}

Ответ 1

Да, для каждой итерации цикла (плюс возможно избыточное toString?), а также плохая практика, вызывать list.size() для каждой итерации и— это значение должно быть кэшировано до начала цикла.

В любом случае, если вы работаете с этим большим количеством данных, есть причина, по которой вы не используете базу данных SQLite для хранения и отображения/фильтрации вашего списка, используя CursorAdapter?

Это был бы рекомендуемый способ реализовать что-то такого размера.

Ответ 2

Может быть, вы можете обменять некоторое пространство на определенную скорость? Создайте какую-либо форму индекса для ваших данных?

Например:

  • Создайте список для каждого символа (a-z) со всеми "MyObject", где часть имени содержит символ (знайте о специальных символах!). Для каждой записи подсчитывается количество "MyObject" s
  • Если пользователь вводит запрос, найдите отдельные символы и только найдите список с наименьшим количеством записей.

Конечно, добавление имени потребует, чтобы вы добавили его в индекс.

Ответ 3

После нескольких исследований я обнаружил, что Suffix Arrays может получить ответы на посты. Также посмотрите на статью Wikipedia для Suffix Trees для более подробного объяснения.
Besdies, что я согласен с ответом выше, что вы, вероятно, могли бы использовать базу данных SQL для таких запросов. Выполнение запроса Sql против данных, вероятно, является одним из самых быстрых способов получить то, что вы хотите, без массивов суффикса.
Одна вещь, чтобы немного ускорить работу без выполнения SQL, заключалась бы в том, чтобы поместить firstName, middleName, lastName в одну строчную строку и поместить ее в новую карту, ссылающуюся на индекс массива. Таким образом, вы можете сократить поиск до 10 000 строк хэш-карты, не делая при этом каждый раз строчный регистр. Это может быть немного быстрее, но, конечно, требуется больше памяти. Возможно, попробуйте сделать что-то с регулярными выражениями, чтобы ускорить сопоставление.
Другим вариантом было бы действительно создать поисковыйиндекс с чем-то вроде Lucene, хотя я думаю, что это действительно было бы излишним на устройстве Android, но могло бы работа в простой Java и инкрементный поиск в Lucene также не являются очень высокой производительностью.

Ответ 4

может быть слишком запоздалым ответом, но он помогает другим в застрявшей же проблеме.

Java 8 (2014) решает эту проблему, используя потоки и lambdas в одной строке кода:

Используя Stream Api, вы можете фильтровать данные без цикла, и доступны дополнительные функции.

List<MyObject> mFilteredMyObjectList = mMyObjectList.stream()
    .filter(d -> d.getFirstName().toString().toLowerCase().indexOf(sv) >= 0 
    || d.getMiddleName().toString().toLowerCase().indexOf(sv) >= 0 
    || d.getLastName().toString().toLowerCase().indexOf(sv) >= 0).collect(Collectors.toList());

Подробнее см. ниже ссылку

Link1  Link2

Ответ 5

Как вы изначально извлекаете список из 10 000+? Если вы просто используете экземпляр SQLite, я бы настоятельно рекомендовал вам сделать это в SQL.