首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >在Java中实现去抖动

在Java中实现去抖动
EN

Stack Overflow用户
提问于 2011-01-20 08:10:10
回答 9查看 21.1K关注 0票数 27

对于我正在编写的一些代码,我可以使用debounce在Java语言中的一个很好的通用实现。

代码语言:javascript
复制
public interface Callback {
  public void call(Object arg);
}

class Debouncer implements Callback {
    public Debouncer(Callback c, int interval) { ... }

    public void call(Object arg) { 
        // should forward calls with the same arguments to the callback c
        // but batch multiple calls inside `interval` to a single one
    }
}

当使用相同的参数在interval毫秒内多次调用call()时,回调函数应该只调用一次。

可视化:

代码语言:javascript
复制
Debouncer#call  xxx   x xxxxxxx        xxxxxxxxxxxxxxx
Callback#call      x           x                      x  (interval is 2)

  • 是否(类似于)已经存在于某些Java标准库中?
  • 您将如何实现?
EN

回答 9

Stack Overflow用户

回答已采纳

发布于 2014-01-08 02:22:38

请考虑以下线程安全解决方案。请注意,锁的粒度是在键级别上的,因此只有对同一键的调用才会相互阻塞。它还处理在调用call(K)时发生的密钥K过期的情况。

代码语言:javascript
复制
public class Debouncer <T> {
  private final ScheduledExecutorService sched = Executors.newScheduledThreadPool(1);
  private final ConcurrentHashMap<T, TimerTask> delayedMap = new ConcurrentHashMap<T, TimerTask>();
  private final Callback<T> callback;
  private final int interval;

  public Debouncer(Callback<T> c, int interval) { 
    this.callback = c;
    this.interval = interval;
  }

  public void call(T key) {
    TimerTask task = new TimerTask(key);

    TimerTask prev;
    do {
      prev = delayedMap.putIfAbsent(key, task);
      if (prev == null)
        sched.schedule(task, interval, TimeUnit.MILLISECONDS);
    } while (prev != null && !prev.extend()); // Exit only if new task was added to map, or existing task was extended successfully
  }
  
  public void terminate() {
    sched.shutdownNow();
  }
  
  // The task that wakes up when the wait time elapses
  private class TimerTask implements Runnable {
    private final T key;
    private long dueTime;    
    private final Object lock = new Object();

    public TimerTask(T key) {        
      this.key = key;
      extend();
    }

    public boolean extend() {
      synchronized (lock) {
        if (dueTime < 0) // Task has been shutdown
          return false;
        dueTime = System.currentTimeMillis() + interval;
        return true;
      }
    }
      
    public void run() {
      synchronized (lock) {
        long remaining = dueTime - System.currentTimeMillis();
        if (remaining > 0) { // Re-schedule task
          sched.schedule(this, remaining, TimeUnit.MILLISECONDS);
        } else { // Mark as terminated and invoke callback
          dueTime = -1;
          try {
            callback.call(key);
          } finally {
            delayedMap.remove(key);
          }
        }
      }
    }  
  }

和回调接口:

代码语言:javascript
复制
public interface Callback<T> {
    public void call(T t);
}
票数 31
EN

Stack Overflow用户

发布于 2016-07-11 04:09:24

下面是我的实现:

代码语言:javascript
复制
public class Debouncer {
    private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
    private final ConcurrentHashMap<Object, Future<?>> delayedMap = new ConcurrentHashMap<>();

    /**
     * Debounces {@code callable} by {@code delay}, i.e., schedules it to be executed after {@code delay},
     * or cancels its execution if the method is called with the same key within the {@code delay} again.
     */
    public void debounce(final Object key, final Runnable runnable, long delay, TimeUnit unit) {
        final Future<?> prev = delayedMap.put(key, scheduler.schedule(new Runnable() {
            @Override
            public void run() {
                try {
                    runnable.run();
                } finally {
                    delayedMap.remove(key);
                }
            }
        }, delay, unit));
        if (prev != null) {
            prev.cancel(true);
        }
    }

    public void shutdown() {
        scheduler.shutdownNow();
    }
}

示例用法:

代码语言:javascript
复制
final Debouncer debouncer = new Debouncer();
debouncer.debounce(Void.class, new Runnable() {
    @Override public void run() {
        // ...
    }
}, 300, TimeUnit.MILLISECONDS);
票数 18
EN

Stack Overflow用户

发布于 2011-01-20 08:20:04

我不知道它是否存在,但它应该很容易实现。

代码语言:javascript
复制
class Debouncer implements Callback {

  private CallBack c;
  private volatile long lastCalled;
  private int interval;

  public Debouncer(Callback c, int interval) {
     //init fields
  }

  public void call(Object arg) { 
      if( lastCalled + interval < System.currentTimeMillis() ) {
        lastCalled = System.currentTimeMillis();
        c.call( arg );
      } 
  }
}

当然,这个示例有点过于简化了,但这或多或少就是您所需要的。如果您想为不同的参数保持单独的超时,那么您将需要一个Map<Object,long>而不仅仅是一个long来跟踪最后一次执行时间。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4742210

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档