在Java中对列表进行增量过滤
我正在处理一个项目,该项目需要我根据用户输入的查询通过名称过长的联系人列表。当我仍在过滤列表时,用户可以输入和删除字符。例如,我可能有一个包含5000个联系人的列表:在Java中对列表进行增量过滤
FirstName1 LastName1
FirstName2 LastName2
...
FirstName5000 LastName5000
用户有一种形式,他/她可以输入搜索条件,清单应缩小到只显示那些符合搜索条件的联系人。这里是我有问题,如果用户输入说
J
我应该过滤列表,只显示其名字或姓氏以“J”的联系人。但是,用户可能会输入另一个字符或删除字符,在这种情况下,我需要重新开始对列表进行过滤。我的问题当然是我想以一种有效的方式做到这一点,而不是等到用字母'J'完成过滤之后才开始用新标准过滤。任何想法/建议?
为避免启动太多的查询,这些查询应该有助于提高可伸缩性,我建议在启动查询之前实现一个等待一定时间的机制。只要用户在此时间段内修改了字段的内容,就会中止先前的查询并安排新的查询。
类似的东西:
代码,创建定时器和预定任务:
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
为下一:
// 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);
代码取消以前计划任务:
创建的ScheduledExecutorService
和安排任务代码(要启动的用户修改任何时候的东西)
// 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);
注:这些代码片段只是为了显示想法,他们不是我蚂蚁是完美的
由于两种方法都等待1秒(对于UI而言很大)并且不会停止当前正在执行的任务。第二种方法中的巨大问题,因为您将执行程序声明为单一线程:以下查询将等待先前的 – JohnnyAW
@JohnnyAW thx给出反对票的原因。 1.这些代码片段仅仅是为了展示这个想法,只有OP可以决定什么是最好的形式,所以1秒只是一个随机值,我可以放400毫秒或任何你想要的,答案的主要思想仍然是一样的。 2.我取消任务是你可以做的最好的任务,因为你不能停止一项任务,你只能检查它是否在执行任务时被中断,如果是的话则中断任务。 3.因为我们只有一个字段,所以我们只需要一个线程,因为我们显然不想在parralel中执行多个查询。 –
1:为什么您甚至使用延迟?高于100-200毫秒的所有信息都会导致用户遇到输入延迟。 2:你为什么没有展示如何检查任务是否被取消? 3:我认为你没有在第二种方法中遇到大问题:如果你不检查取消,你的下面的查询将等待第一个查询来完成搜索,这正是OP想要避免的!你不需要2个线程,但你必须检查取消 – JohnnyAW
好的,所以基本上你需要在后台线程上运行你的查询,并取消当前运行的查询,如果用户更改输入并开始新的。 首先我们需要一个任务类,抚慰你的查询:
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线程上调用以避免设置问题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;
什么是您的用户界面(手机或网络)? –
你基本上可以使用任务来更新已经过滤的结果的队列,然后根据这些进行细化(而你的“主要搜索者”只根据最新的查询在队列中放置新的队列) – Rogue
@ShlomiHaver它是移动的。 Android –