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();
    }
}
KO 及缓存数据结构

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

Scroll to top
粤ICP备2020114259号 粤公网安备44030402004258