JAVA与JavaScript递增ID压缩与解压

上周代码分享中的一个代码,因业务需要要将一组数据库自增的id, 思路就是将id排序,取最小的为基数, 取剩下所有数与基数的差值 新建一个数组将差值的下标改为1 其他默认为0 将这个数组 拆分4个一组 转换为16进制 .
压缩效果如下:

压缩后 = 10zd3e9fffffffffffffffffffffffc
ID个数 = 104
解压后 = [10,11,13,16,17,18,19,20,22,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119]

JAVA实现

import cn.hutool.core.util.ArrayUtil;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.LongSummaryStatistics;
import java.util.stream.Collectors;


/**
 * 一组压缩算法的ID
 * <br/>
 * 类似 10,12,13,33这种类似递增的Id  相差不要太大
 *
 * @author zhangqi
 * @date 2022/5/4 18:09
 */
public final class CompressionIdUtil {
    private CompressionIdUtil() {
    }

    /**
     * 分隔符
     */
    public static final String SEPARATOR = "z";
    /**
     * 连续8个灵替换 x
     */
    public static final String ZERO = "x";
    /**
     * 连续8个灵替换 x
     */
    public static final String EIGHT_ZERO = "00000000";

    /**
     * 压缩算法ID
     *
     * @param ids 压缩ID
     * @return 压缩后
     */
    public static String compression(long... ids) {
        if (ArrayUtil.isEmpty(ids)) {
            return "";
        }
        LongSummaryStatistics arrResult = Arrays.stream(ids.clone()).distinct().sorted().summaryStatistics();
        long count = arrResult.getCount();
        long minId = arrResult.getMin();
        if (count == 1) {
            return minId + SEPARATOR + 1;
        }
        long maxId = arrResult.getMax();
        long[] newIds = new long[(int) (Math.ceil(Math.max((((int) (maxId - minId)) >> 3) + 1, 1)) * 8)];
        Arrays.stream(ids).forEach(id -> newIds[(int) (id - minId)] = 1);
        List<String> compressionList = Arrays.stream(newIds).mapToObj(String::valueOf).collect(Collectors.toList());
        return minId + SEPARATOR + Lists.partition(compressionList, 4).stream()
                .map(item -> Integer.toString(Integer.parseInt(String.join("", item), 2), 16))
                .collect(Collectors.joining()).replace(EIGHT_ZERO, ZERO);
    }

    /**
     * 解压ID
     *
     * @param compressionStr 压缩后的ID
     * @return 解压ID后
     */
    public static List<Long> unpack(String compressionStr) {
        if (StringUtils.isBlank(compressionStr)) {
            return Collections.emptyList();
        }
        compressionStr = compressionStr.replace(ZERO, EIGHT_ZERO);
        String[] strArr = compressionStr.split(SEPARATOR);
        if (strArr.length != 2) {
            return Collections.emptyList();
        }
        String baseId = strArr[0];
        char[] chars = strArr[1].toCharArray();
        long id = Long.parseLong(baseId);
        if (chars.length == 1) {
            return Collections.singletonList(id);
        }
        StringBuilder compression = new StringBuilder();
        for (char aChar : chars) {
            compression.append(StringUtils.leftPad(Integer.toString(Integer.parseInt(String.valueOf(aChar), 16), 2), 4, "0"));
        }
        List<Long> resultList = Lists.newArrayListWithCapacity(compressionStr.length() >> 2);
        for (int i = 0; i < compression.toString().toCharArray().length; i++) {
            if (compression.toString().toCharArray()[i] == '1') {
                resultList.add(id + i);
            }
        }
        return resultList;
    }


    public static void main(String[] args) {
        String compression = compression(10, 16, 11, 13, 17, 18, 19, 20, 22, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 2000);
        System.out.println("compression = " + compression);
        List<Long> unpack = unpack(compression);
        int size = unpack.size();
        System.out.println("size = " + size);
        String s = JSONObject.toJSONString(unpack);
        System.out.println("s = " + s);
    }
}

结果:

compression = 10zd3e9fffffffffffffffffffffffcxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx000002
size = 105
s = [10,11,13,16,17,18,19,20,22,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,2000]

JS实现 没有实现2次替换

 const byteToHex = (s) => {
        const r = parseInt(s, 2).toString(16);
        return r;
    };
    //16进制重新变成2进制
    const hexToByte = (s) => {
        const r = parseInt(s, 16).toString(2);
        return r;
    };

    //压缩
    export const arrayToStr = (arr) => {
        arr.sort(function (a, b) {
            return +a - +b;
        });
        const baseId = arr[0];
        const indexArr = arr.map((o) => +o - +baseId);
        const len = +indexArr[indexArr.length - 1] + 1;
        const newArr = new Array(Math.ceil(len / 8) * 8).fill(0); //每8位为一个字节,不足的补0
        indexArr.forEach((o) => (newArr[+o] = 1));
        let strArr = Array(newArr.length / 4)
            .fill(0)
            .map((o) => (o = newArr.splice(0, 4).join('')));
        let data = strArr.map((o) => byteToHex(o)).join('');
        return `${data}z${baseId}`;
    };


    //解码
    export const strToArray = (s) => {
        let baseId = s.substr(s.indexOf('z') + 1);
        let strArr = s.substr(0, s.indexOf('z'));
        let arr = strArr.split('');
        let m = new Array(arr.length).fill(0).map((o) => hexToByte(arr.splice(0, 1)));
        let newArr = m
            .map((o) => o.padStart(4, '0'))
            .join('')
            .split('');
        let indexArr = [];
        newArr.forEach((o, i) => o === '1' && indexArr.push(i));
        indexArr = indexArr.map((o) => `${String(+baseId + o)}`);
        return indexArr;
    };
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值