KO 及缓存数据结构

背景

说到 KO,你以为是这样的吗?

图片来自网络

先回顾一下之前的分享:

java 移位运算

由于缓存的 key 由多个属性组成,就涉及到了缓存数据结构的设计

  • 多维 map

  • 一维 map + 组合 key

在那篇文章里,由于多个属性都是 int,最终选择了组合 key 的方案。

那么,如果多个属性不止是 int,还有 String,该怎么处理呢?多维 map 和 一维 map 如何取舍呢?

多维 map

guava 提供了 Table 接口,实际上是个二维 map,查看 guava 的源码可以发现,这个 Table 还是很重量级的,如果你的需求仅仅是 put(), get(),那我不建议使用 Table

一维 map + 组合 key

所以本文主要研究组合 key 的情况,并提出一个专用于组合 key 的对象: KO,含义为 key object

KO

一个标准的 KO 应该满足如下条件

  • 继承自 Object,且为 final 类

  • 必须提供带参数的构造方法,且该方法为唯一的构造方法

  • 建议使用静态工厂方法来创建实例,此时构造方法应为私有的

  • 在构造方法里计算出 hashcode

  • 类属性是 final 的

  • 必须重写 hashCode(), equals()

  • 建议重写 toString()

示例:一个组合 imei(整数),sn(字符串)的 KO

import java.util.Objects;

public final class ImeiSnKo {

    private final Long   imei;
    private final String sn;
    private final int    hashCode;


    public static final ImeiSnKo create(Long imei, String sn) {
        return new ImeiSnKo(imei, sn);
    }


    private ImeiSnKo(Long imei, String sn) {
        this.imei = imei;
        this.sn = sn;

        this.hashCode = 31 * Objects.hashCode(imei) + Objects.hashCode(sn);
    }


    @Override
    public int hashCode() {
        return hashCode;
    }


    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj instanceof ImeiSnKo) {
            ImeiSnKo ko = (ImeiSnKo) obj;
            return Objects.equals(imei, ko.imei) && Objects.equals(sn, ko.sn);
        }
        return false;
    }

}

KO 的优势

和拼接字符串构造 key 来对比

  • 含义明确,提升代码可读性

  • 性能优势:无需进行字符串拼接操作

  • 内存占用优势:没有产生新的字符串对象

和通过移位构造 key 来对比

  • 代码可读性的提升是明显的,移位运算确实很难理解

  • 性能上应该还是移位更优

  • 消除隐患:移位运算要小心计算移动的位数,避免随着业务发展构造 key 的整数值突破预留的位数,KO 完全不需要担心,下面示例一下我改造后的代码

public final class TripleIntKey {

    private final int i;
    private final int j;
    private final int k;

    private final int hash;


    public static final TripleIntKey create(int i, int j, int k) {
        return new TripleIntKey(i, j, k);
    }


    private TripleIntKey(int i, int j, int k) {
        this.i = i;
        this.j = j;
        this.k = k;
        this.hash = 31 * (31 * i + j) + k;
    }


    @Override
    public int hashCode() {
        return hash;
    }


    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }

        if (obj instanceof TripleIntKey) {
            TripleIntKey ko = (TripleIntKey) obj;
            return i == ko.i && j == ko.j && k == ko.k;
        }
        return false;
    }


    @Override
    public String toString() {
        return new StringBuilder().append(i).append('-').append(j).append('-').append(k).toString();
    }
}

Last updated