java ConcurrentHashMap computeIfAbsent告诉是否是首次

ccgok5k5  于 2022-12-28  发布在  Java
关注(0)|答案(2)|浏览(152)

我很难给它起一个合适的标题,但举个例子应该会简单得多,假设我有这样的例子:

final class Cache {
   private static final ConcurrentHashMap<String, List<String>> CACHE = ...

   static String byName(String name) {
      return CACHE.computeIfAbsent(name, x -> // some expensive operation)
   }

}

这个想法可能是微不足道的,它充当一个LoadingCache,很像Guava或咖啡因(实际上它更复杂,但这与问题无关)。
我希望能够判断这是第一次加载该高速缓存,还是读取了现有Map。目前,我这样做:

final class Cache {
   private static final ConcurrentHashMap<String, List<String>> CACHE = ...

   static String byName(String name) {
      boolean b[] = new boolean[1];
      List<String> result = CACHE.computeIfAbsent(name, x -> {
            b[0] = true;
            // some expensive operation)
      });

      if(b[0]) {
         // first load into the cache, do X
      } else {
         // do Y
      }

      return result;
   }

}

这是可行的,但我担心我错过了ConcurrentHashMap可以为我提供的东西,它可以让我做同样的事情。谢谢。

rta7y2nd

rta7y2nd1#

如果你想避免使用单元素数组将数据从lambda中传递出去(我更愿意使用AtomicReferenceAtomicBoolean),你可以使用一个有状态回调对象,它不会改变你代码的行为或设计,但可以被认为是更干净和更面向对象的。

class LoadingAction<K, V> {
  private boolean called = false;

  public V load(final K key) {
    called = true;
    // load data
    return ...;
  }

  public void executePostLoad() {
    if (called) {
      // loaded into cache, do X
    } else {
      // do Y
    }
  }
}

final class Cache {
   private static final ConcurrentHashMap<String, List<String>> CACHE = new ConcurrentHashMap<>();

   static List<String> byName(String name) {
      final LoadingAction<String, List<String>> loader = new LoadingAction<>();
      final List<String> result = CACHE.computeIfAbsent(name, loader::load);

      loader.executePostLoad();

      return result;
   }

}

或者把它翻过来:

class Loader<K, V> {
  private boolean called = false;

  public V load(final Map<K, V> map, final K key) {
    final V result = map.computeIfAbsent(key, this::load);
    this.executePostLoad();
    return result;
  }

  private V load(final K key) {
    called = true;
    // load data
    return ...;
  }

  private void executePostLoad() {
    if (called) {
      // loaded into cache, do X
    } else {
      // do Y
    }
  }
}

final class Cache {
   private static final ConcurrentHashMap<String, List<String>> CACHE = new ConcurrentHashMap<>();

   static List<String> byName(String name) {
      final Loader<String, List<String>> loader = new Loader<>();
      return loader.load(CACHE, name);
   }

}

构造和加载可以封装在静态方法中:

class Loader<K, V> {
  private boolean called = false;

  public static <K, V> V load(final Map<K, V> map, final K key) {
      final Loader<K, V> loader = new Loader<>();
      return loader.doLoad(map, key);
  }

  private V doLoad(final Map<K, V> map, final K key) {
    final V result = map.computeIfAbsent(key, this::load);
    this.executePostLoad();
    return result;
  }

  private V load(final K key) {
    called = true;
    // load data
    return ...;
  }

  private void executePostLoad() {
    if (called) {
      // loaded into cache, do X
    } else {
      // do Y
    }
  }
}

final class Cache {
   private static final ConcurrentHashMap<String, List<String>> CACHE = new ConcurrentHashMap<>();

   static List<String> byName(String name) {
      return Loader.load(CACHE, name);
   }

}
jei2mxaa

jei2mxaa2#

我可以想到几种使用ConcurrentHashMap API的方法。这两种方法都使用了一个有副作用的Map函数。其思想是函数记录 * 是否 * 调用了它,或者 * 使用了什么参数 * 调用了它。
computeIfAbsent的规范说明只有在键不存在时才调用Map函数。或者,compute的规范说明如果键是参数,则使用null参数调用Map函数。在任一种情况下,如果您通过(比如)Map器函数/对象的字段,您可以确定该高速缓存条目是否已经存在。
要使此函数线程安全,需要创建mapper函数的一个新示例(线程限制的)。

相关问题