Java中列表的增量过滤

时间:2016-08-11 17:26:38

标签: java multithreading list filter

我正在开展一个项目,要求我根据用户输入的查询按名称过滤一长串联系人。当我仍在过滤列表时,用户可以输入和删除字符。例如,我可能有一个包含5000个联系人的列表:

FirstName1  LastName1
FirstName2  LastName2
...
FirstName5000 LastName5000

用户有一个表单,他/她可以输入搜索条件,列表应缩小以仅显示符合搜索条件的联系人。如果用户输入了

,我遇到了问题
J

我应该过滤列表,只显示名字或姓氏以' J'开头的联系人。但是,用户可能会输入另一个字符或删除字符,在这种情况下,我需要重新启动列表的过滤。我的问题当然是我希望以一种有效的方式做到这一点,而不是等到过滤完成后用字母' J'在我开始使用新标准过滤之前。有什么想法/建议吗?

2 个答案:

答案 0 :(得分: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:这些代码段只是为了表明这个想法,它们并不是完美的

答案 1 :(得分:0)

好的,所以基本上你需要在后台线程上运行你的查询,并在用户更改输入并启动一个新查询时取消当前运行的查询。 首先,我们需要一个任务类,它将包装您的查询:

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;