2016-08-11 4 views
3

Я работаю над проектом, который требует от меня фильтрации через длинный список контактов по имени на основе запроса, введенного пользователем. Пользователь может вводить и удалять символы, пока я все еще фильтрую список. Например, я мог бы иметь список, содержащий 5000 контактов:Инкрементная фильтрация списка в Java

FirstName1 LastName1 
FirstName2 LastName2 
... 
FirstName5000 LastName5000 

Пользователь имеет форму, где он/она может ввести критерии поиска и список должен сжиматься, чтобы показать только те контакты, которые соответствуют критериям поиска. Вот проблема у меня есть, если пользователь вводит сказать

J 

я должен фильтровать список и показывать только контакты, чьи первое имя или фамилия начинается с «J». Однако пользователь может затем ввести другой символ или удалить символы, и в этом случае мне нужно перезапустить фильтрацию списка. Конечно, проблема заключается в том, что я хочу сделать это эффективным образом и не дожидаться, пока фильтрация будет выполнена с буквой «J», прежде чем я начну фильтровать новые критерии. Любые идеи/рекомендации?

+0

Какое у вас (мобильное или сетевое)? –

+0

вы могли бы по существу использовать задачи, которые обновляют очередь уже отфильтрованных результатов, а затем просто уточняют на основе этих (в то время как ваш «первичный искатель» помещает только новые в очередь на основе последнего запроса) – Rogue

+0

@ShlomiHaver Мобильный. Android –

ответ

0

Чтобы избежать слишком большого количества запросов, которые должны быть более масштабируемыми, я бы предложил реализовать механизм, ожидающий заданного промежутка времени перед запуском запроса. В любое время, когда пользователь изменяет содержимое поля в течение этого временного интервала, он прервет предыдущий запрос и заплатит новый запрос.

Нечто подобное:

код, который создает таймер и запланировать задачу:

Timer timer = new Timer(); 
// Schedule my task to be executed in 200 milliseconds 
timer.schedule(new TimerTask() { 
    @Override 
    public void run() { 
     // Launch my query here 
    } 
}, 200L); 

Код для отмены предыдущей запланированной задачи: (запуск в любое время, в модифицируют пользователь что-то)

// Cancel the previous timer which will also abort the scheduled task 
timer.cancel(); 
// Create a new timer 
timer = new Timer(); 
// Re-schedule the task 
timer.schedule(new TimerTask() { 
    @Override 
    public void run() { 
     // Launch my query here 
    } 
}, 200L); 

Это также можно сделать с помощью ScheduledExecutorService в следующем:

код, который создает ScheduledExecutorService и запланировать задачу:

// Create the ScheduledExecutorService 
ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); 
// Submit the task to be executed in 200 milliseconds 
ScheduledFuture<?> future = executor.schedule(new Runnable() { 
    @Override 
    public void run() { 
     // Launch my query here 
    } 
}, 200, TimeUnit.MILLISECONDS); 

Код для отмены предыдущей запланированной задачи: (запуск в любое время пользователя изменяет что-то)

// Cancel the task which will interrupt the thread that was executing the 
// task if any 
future.cancel(true); 
// Re-submit the task 
future = executor.schedule(new Callable<Void>() { 
    @Override 
    public Void call() throws InterruptedException { 
     ... 
     // Check regularly in your code if the thread has been 
     // interrupted and if so throws an exception to stop 
     // the task immediately 
     if (Thread.currentThread().isInterrupted()) { 
      throw new InterruptedException("Thread interrupted"); 
     } 
     ... 
    } 
}, 200, TimeUnit.MILLISECONDS); 

NB: Эти фрагменты кода предназначены только для того, чтобы показать идею, они не меня ant быть совершенным

+0

down-vote, потому что оба метода ждут 1 секунду (огромные для пользовательского интерфейса) И не останавливают выполнение текущей задачи. Огромная проблема во втором методе, так как вы объявили исполнителя как единый Threaded: следующий запрос будет ждать предыдущего – JohnnyAW

+0

@JohnnyAW thx за то, что он дал причину проголосовавшего. 1. Эти фрагменты кода предназначены только для того, чтобы показать идею не более того, что ОП может решить, какая из них лучшая, поэтому 1 сек - это только случайное значение, которое я мог бы поставить 400 мс или что бы вы ни хотели, основная идея ответа все равно будет одинаковым. 2. Я отменяю задачу - это лучшее, что вы можете сделать, так как вы не можете остановить задачу, вы можете только проверить, прервано ли она при выполнении задачи и прервать задачу, если это так. 3. Поскольку у нас есть одно поле, нам нужен только один поток, поскольку мы явно не хотим выполнять несколько запросов в parralel. –

+0

1: почему вы даже используете задержку? все выше 100-200 мс вызывает у пользователя ощущение задержки ввода. 2: почему вы не показали, как проверить отмену задания? 3: Я думаю, что вы не получили огромную проблему во втором методе: если вы не будете проверять отмену, ваши следующие запросы будут ждать, пока первый запрос завершит поиск, и это именно то, что OP хочет избежать! Вам не нужны 2 нити, но вы должны проверить отмену – JohnnyAW

0

ok, поэтому в основном вам нужно запустить запрос в фоновом потоке и отменить текущий запрос, если пользователь меняет ввод и запускает новый. первых нам нужен класс задач, который будет обернуть запрос:

class CancelableTask implements Callable<Void> { 
    //need this to know, if the task was canceled 
    private Future<Void> myFuture; 


    public void setMyFuture(Future<Void> myFuture) { 
     this.myFuture = myFuture; 
    } 


    @Override 
    public Void call() throws Exception { 
     //we run a loop until the query is finished or task was canceled 
     while (!this.myFuture.isCancelled() && !myQuery.isFinished()) { 
      //the step should be small enough to fast detect task cancellation but big enough to avoid too much overhead 
      myQuery.performQueryStep(); 
     } 
     if(!this.myFuture.isCancelled()){ 
      //query is finished and task wasn't canceled, so we should update UI now 
      updateUIOnUIThread(myQuery.result()); 
     } 
     return null; 
    } 
} 

теперь вам нужно создать ExecutorService где-то в вашей деятельности:

//1 Thread should be enough, you could use 2 Threads if your query-step is quite long and you want to start the following query faster 
private ExecutorService executor = Executors.newSingleThreadExecutor(); 

теперь мы можем использовать executor для выполнения задач. Этот код следует вызвать, как только пользователь изменит ввод.Его следует называть на UI-Thread, чтобы избежать проблем с настройкой currentTaskFuture:

//check if need to cancel the currentTask 
if(currentTaskFuture != null && !currentTaskFuture.isDone()){ 
    currentTaskFuture.cancel(false); 
} 

CancelableTask task = new CancelableTask(); 

//submit the task 
Future<Void> future = executor.submit(task); 
task.setMyFuture(future); 
//set current task's future so we can cancel it if needed 
currentTaskFuture = future; 
Смежные вопросы