专栏首页10km的专栏guava:Cache中使用数组(Object[],int[]...)作为KEY

guava:Cache中使用数组(Object[],int[]...)作为KEY

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/10km/article/details/103072235

guava是google的一个开源的基础java库,其中提供了一个非常有用的缓存(cache)功能。创建cache的过程大概如下:

LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
       .maximumSize(1000)
       .expireAfterWrite(10, TimeUnit.MINUTES)
       .removalListener(MY_LISTENER)
       .build(
           new CacheLoader<Key, Graph>() {
             @Override
             public Graph load(Key key) throws AnyException {
               return createExpensiveGraph(key);
             }
           });

本文的目标不是介绍如何使用guava 的cache,我们知道guava 的cache是基于ConcurrentMap来实现的,但我们也知道Map中不能使用数组(Object[],int[]…)作为key,所以在创建Cache对象时,自然也不能使用数组作为Key。如果希望把一组对象作为Key,可以考虑用把数组封装为List作为Key.

最近在我的一个项目,出于效率考虑,我就是希望用Object[]作为Key.能不能实现呢?

要解决这个问题,首先要知道为什么不能用数组做Map的key。数组的equals方法只是简单比较两个数组指针是否一样,并不比较数组中的元素,所以不能正确判断两个数组相等,hashCode方法则只是根据对象指针的地址计算,所以数组类型的equals和hashCode方法的计算结果不能作为Map识别Key的依据。

所以只要对数组对象能正确计算hash code,正确比较相等,Map也是可以用数组做key的。

仔细研究com.google.common.cache.LocalCache的源码,可以知道,LocalCache是使用Equivalence对象实现对象比较和哈希码计算的,参见com.google.common.cache.LocalCache.Segment.getEntry(Object key, int hash),代码如下,keyEquivalence就是用于Key等价计算的Equivalence对象:

    ReferenceEntry<K, V> getEntry(Object key, int hash) {
      for (ReferenceEntry<K, V> e = getFirst(hash); e != null; e = e.getNext()) {
        if (e.getHash() != hash) {
          continue;
        }

        K entryKey = e.getKey();
        if (entryKey == null) {
          tryDrainReferenceQueues();
          continue;
        }

        if (map.keyEquivalence.equivalent(key, entryKey)) {
          return e;
        }
      }

      return null;
    }

进一步研究com.google.common.cache.CacheBuilder的代码,找到了如下代码,哈,原来CacheBuilder可以指定Equivalence,如果不指定就使用默认值:

  /**
   * Sets a custom {@code Equivalence} strategy for comparing keys.
   *
   * <p>By default, the cache uses {@link Equivalence#identity} to determine key equality when
   * {@link #weakKeys} is specified, and {@link Equivalence#equals()} otherwise.
   *
   * @return this {@code CacheBuilder} instance (for chaining)
   */
  @GwtIncompatible // To be supported
  CacheBuilder<K, V> keyEquivalence(Equivalence<Object> equivalence) {
    checkState(keyEquivalence == null, "key equivalence was already set to %s", keyEquivalence);
    keyEquivalence = checkNotNull(equivalence);
    return this;
  }

所以只要我们实现实现一个Equivalence对象,通过上面这个CacheBuilder.keyEquivalence(Equivalence<Object> equivalence) 方法传递给LocalCache,就可以用数组做key了。

但是CacheBuilder.keyEquivalence(Equivalence<Object> equivalence) 方法的访问修饰符不是public,所以无法在外部访问,解决这个并不难,如下在com.google.common.cache包下创建一个类就调用CacheBuilder.keyEquivalence(Equivalence<Object> equivalence) 方法就可以了。

所以完整的实现代码如下:

package com.google.common.cache;

import java.util.Arrays;
import java.util.Objects;

import com.google.common.base.Equivalence;

public class DeepCacheBuilder {
	private static final Equivalence<Object> DEEP_EQUIVALENCE = new Equivalence<Object>(){

		@Override
		protected boolean doEquivalent(Object a, Object b) {
			return Objects.deepEquals(a, b);
		}

		@Override
		protected int doHash(Object object) {
			return deepHashCode(object);
		}};
		
	public static final int deepHashCode(Object a){
		if (a == null){
            return 0;
		}else if (a instanceof Object[]) {
			return Arrays.deepHashCode((Object[]) a);
		} else if (a instanceof byte[]) {
			return Arrays.hashCode((byte[]) a);
		} else if (a instanceof short[]) {
			return Arrays.hashCode((short[]) a);
		} else if (a instanceof int[]) {
			return Arrays.hashCode((int[]) a);
		} else if (a instanceof long[]) {
			return Arrays.hashCode((long[]) a);
		} else if (a instanceof char[]) {
			return Arrays.hashCode((char[]) a);
		} else if (a instanceof float[]) {
			return Arrays.hashCode((float[]) a);
		} else if (a instanceof double[]) {
			return Arrays.hashCode((double[]) a);
		} else if (a instanceof boolean[]) {
			return Arrays.hashCode((boolean[]) a);
		} 
		return a.hashCode();
	}
	public static final CacheBuilder<Object, Object> newBuilder(){
		return newBuilder(DEEP_EQUIVALENCE,DEEP_EQUIVALENCE);
	}
	public static final CacheBuilder<Object, Object> newBuilder(Equivalence<Object> keyEquivalence,Equivalence<Object> valueEquivalence){
		CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder();
		if(keyEquivalence != null){
			builder.keyEquivalence(keyEquivalence);
		}
		if(valueEquivalence != null){
			builder.valueEquivalence(valueEquivalence);	
		}
		return builder;
	}
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • java:java.util.Map和java.util.Set的Key类型转换

    google的guava项目提对Map供了丰富强大的转换功能(参见guava jar包中的com.google.common.collect.Maps ) ...

    用户1148648
  • java:均值哈希实现图像内容相似度比较

    这阵子发现我的图像数据库中有不少内容一样的图像需要剔除,这些内容一样的图像可能尺寸不一样,通道数也可能不一样(灰度/彩色),如下三张图内容完全一样,只是亮度或色...

    用户1148648
  • linux gnu c 复制文件实例(open,close,creat,read,write)

    版权声明:本文为博主原创文章,转载请注明源地址。 https://blog.csdn.net...

    用户1148648
  • Guava-1.16类Joiner

    一个用分隔符合并文本片段的类,也可以合并数组,迭代,变量,甚至map。 可以将结果添加到一个Appendable类或者直接返回String。 如果没有指定s...

    悠扬前奏
  • SSM动态切换数据源

    这里默认大家都会SSM框架了,使用时我们要往sqlSessionFactory里注入数据源。那么猜测:1、可以往sqlSessionFactory里注入多数据源...

    晚上没宵夜
  • Web 安全漏洞 SSRF 简介及解决方案

    Update: 掘金评论区有同学提出通过域名获取 IP 地址时可能遭遇攻击,感谢提醒。本人非安全专业相关人士,了解不多,实在惭愧。

    逆葵
  • 面试题19(关于return的用法)

    执行下列代码的输出结果是? public class Demo { public static void main(String args[]) { int...

    Java学习
  • RefineDetLite:腾讯提出轻量级高精度目标检测网络

    前几天腾讯公布了一篇论文RefineDetLite: A Lightweight One-stage Object Detection Framework fo...

    CV君
  • 用户研究之怎样做好用户访谈?

    用户1756920
  • asp.net mvc项目实记-开启伪静态-Bundle压缩css,js

    js //定义自己的规则 var myXssOptions = function (isEditer) { retur...

    易墨

扫码关注云+社区

领取腾讯云代金券