Use a TopKeyMap in HitBooleanMap also

Modify the MatchCache to use the new API. This keeps track of the
row/columns of the elements with the top 5 loading times.

Change-Id: Ic488db4012fdca9700d2c0c9c9348cf7fa0e7927
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/HitBooleanTable.java b/src/main/java/com/googlesource/gerrit/plugins/task/HitBooleanTable.java
index adce341..91d7ad4 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/HitBooleanTable.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/HitBooleanTable.java
@@ -21,16 +21,17 @@
  * stored in it are all Booleans and uses BitSets to make this very space efficient.
  */
 public class HitBooleanTable<R, C> extends BooleanTable<R, C> implements TracksStatistics {
-  public static class Statistics {
+  public static class Statistics<V> {
     public long hits;
     public long misses;
     public long size;
     public int numberOfRows;
     public int numberOfColumns;
     public Long sumNanosecondsLoading;
+    public TopKeyMap<V> topNanosecondsLoadingKeys = new TopKeyMap<>();
   }
 
-  protected Statistics statistics;
+  protected Statistics<TopKeyMap.TableKeyValue<R, C>> statistics;
 
   @Override
   public Boolean get(R r, C c) {
@@ -45,19 +46,29 @@
     return value;
   }
 
-  public StopWatch createLoadingStopWatch() {
+  public StopWatch createLoadingStopWatch(R row, C column, boolean isVisible) {
     if (statistics == null) {
       return StopWatch.DISABLED;
     }
     if (statistics.sumNanosecondsLoading == null) {
       statistics.sumNanosecondsLoading = 0L;
     }
-    return new StopWatch.Enabled().setNanosConsumer(ns -> statistics.sumNanosecondsLoading += ns);
+    return new StopWatch.Enabled()
+        .setNanosConsumer(
+            ns ->
+                statistics.sumNanosecondsLoading +=
+                    updateTopLoadingTimes(ns, row, column, isVisible));
+  }
+
+  public long updateTopLoadingTimes(long nanos, R row, C column, boolean isVisible) {
+    statistics.topNanosecondsLoadingKeys.addIfTop(
+        nanos, isVisible ? new TopKeyMap.TableKeyValue<R, C>(row, column) : null);
+    return nanos;
   }
 
   @Override
   public void initStatistics() {
-    statistics = new Statistics();
+    statistics = new Statistics<>();
   }
 
   @Override
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/MatchCache.java b/src/main/java/com/googlesource/gerrit/plugins/task/MatchCache.java
index c7f7e41..df0b921 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/MatchCache.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/MatchCache.java
@@ -46,7 +46,8 @@
     Boolean isMatched = resultByChangeByQuery.get(query, changeData.getId());
     if (isMatched == null) {
       Matchable<ChangeData> matchable = predicateCache.getPredicate(query, isVisible).asMatchable();
-      try (StopWatch stopWatch = resultByChangeByQuery.createLoadingStopWatch()) {
+      try (StopWatch stopWatch =
+          resultByChangeByQuery.createLoadingStopWatch(query, changeData.getId(), isVisible)) {
         isMatched = matchable.match(changeData);
         resultByChangeByQuery.put(query, changeData.getId(), isMatched);
       }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/TopKeyMap.java b/src/main/java/com/googlesource/gerrit/plugins/task/TopKeyMap.java
index a23d59f..5753b52 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/TopKeyMap.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/TopKeyMap.java
@@ -31,6 +31,20 @@
  * insertion approach, it is easy to make a TopKeyMap efficiently thread safe.
  */
 public class TopKeyMap<V> {
+  /**
+   * A TableKeyValue is a helper class for TopKeyMap use cases, such as a table with with row and
+   * column keys, which involve two values.
+   */
+  public static class TableKeyValue<R, C> {
+    public final R row;
+    public final C column;
+
+    public TableKeyValue(R row, C column) {
+      this.row = row;
+      this.column = column;
+    }
+  }
+
   protected class Entry {
     public long key;
     public V value;