前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >guava:Cache中使用数组(Object[],int[]...)作为KEY

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

作者头像
10km
发布2019-11-18 13:53:53
1.5K0
发布2019-11-18 13:53:53
举报
文章被收录于专栏:10km的专栏

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

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

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

代码语言:javascript
复制
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对象:

代码语言:javascript
复制
    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,如果不指定就使用默认值:

代码语言:javascript
复制
  /**
   * 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) 方法就可以了。

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

代码语言:javascript
复制
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;
	}
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019/11/14 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档