使用String存储和分配可用资源

问题

遇到一些资源存储和分配的问题,例如,某种资源的取值范围为1-65535,需要动态分配和回收。在Java中最小的数据类型为byte,如果使用byte[65535]在内存和库中存储,至少需要65KB空间,当网络中此类资源池过多时,会造成内存资源大量消耗。

解决思路

利用String来存储和描述id:1-65535的占用情况。如"2-5,20,30,40-45,105-120,300,400-500,600-700,1000-1050,2000-65535"表示可用id有:2、3、4、5、20、30、40、41、42、43、44、45、105…
在存储和Java内部处理时均以字符串形式处理,不将其转变为byte数组。以节省内存空间。

  1. 字符串格式中以COMMA="," 作为分割符;以CONN="-"作为连接符;
  2. 保持有序排列,这样可以用二分法进行快速查找(这里使用了一种二分法的变形,一直没有做理论推导,但确实好用);

Java实现

类中的方法与属性
其中public方法有四个:
releaseId(int) 释放指定的Id资源;
getId(int) 占用指定的Id资源;
getId() 自由占用Id资源,默认策略是最小值优先。
getIdPool 查看当前的Id资源池

测试效果如下:

idpool is 2-5,20,30,40-45,105-120,300,400-500,600-700,1000-1050,2000-65535
get Id for targetId:2
idpool is 3-5,20,30,40-45,105-120,300,400-500,600-700,1000-1050,2000-65535
get Id for targetId:4
idpool is 3,5,20,30,40-45,105-120,300,400-500,600-700,1000-1050,2000-65535
get Id for targetId:20
idpool is 3,5,30,40-45,105-120,300,400-500,600-700,1000-1050,2000-65535
get Id for targetId:41
idpool is 3,5,30,40,42-45,105-120,300,400-500,600-700,1000-1050,2000-65535
get Id for targetId:43
idpool is 3,5,30,40,42,44-45,105-120,300,400-500,600-700,1000-1050,2000-65535
get Id for targetId:105
idpool is 3,5,30,40,42,44-45,106-120,300,400-500,600-700,1000-1050,2000-65535
get Id for targetId:120
idpool is 3,5,30,40,42,44-45,106-119,300,400-500,600-700,1000-1050,2000-65535
get Id for targetId:650
idpool is 3,5,30,40,42,44-45,106-119,300,400-500,600-649,651-700,1000-1050,2000-65535
id is not in pool
idpool is 3,5,30,40,42,44-45,106-119,300,400-500,600-649,651-700,1000-1050,2000-65535
get Id for default: 3
idpool is 5,30,40,42,44-45,106-119,300,400-500,600-649,651-700,1000-1050,2000-65535
get Id for default: 5
idpool is 30,40,42,44-45,106-119,300,400-500,600-649,651-700,1000-1050,2000-65535
get Id for default: 30
idpool is 40,42,44-45,106-119,300,400-500,600-649,651-700,1000-1050,2000-65535
get Id for default: 40
idpool is 42,44-45,106-119,300,400-500,600-649,651-700,1000-1050,2000-65535
get Id for default: 42
idpool is 44-45,106-119,300,400-500,600-649,651-700,1000-1050,2000-65535
get Id for default: 44
idpool is 45,106-119,300,400-500,600-649,651-700,1000-1050,2000-65535
get Id for default: 45
idpool is 106-119,300,400-500,600-649,651-700,1000-1050,2000-65535
get Id for default: 106
idpool is 107-119,300,400-500,600-649,651-700,1000-1050,2000-65535
get Id for default: 107
idpool is 108-119,300,400-500,600-649,651-700,1000-1050,2000-65535
get Id for default: 108
idpool is 109-119,300,400-500,600-649,651-700,1000-1050,2000-65535
get Id for default: 109
idpool is 110-119,300,400-500,600-649,651-700,1000-1050,2000-65535
get Id for default: 110
idpool is 111-119,300,400-500,600-649,651-700,1000-1050,2000-65535
get Id for default: 111
idpool is 112-119,300,400-500,600-649,651-700,1000-1050,2000-65535
get Id for default: 112
release id: 100
idpool is 100,113-119,300,400-500,600-649,651-700,1000-1050,2000-65535
release id: 99
idpool is 99-100,113-119,300,400-500,600-649,651-700,1000-1050,2000-65535
release id: 98
idpool is 98-100,113-119,300,400-500,600-649,651-700,1000-1050,2000-65535
release id: 101
idpool is 98-101,113-119,300,400-500,600-649,651-700,1000-1050,2000-65535
release id: 101
101 has been in pool
idpool is 98-101,113-119,300,400-500,600-649,651-700,1000-1050,2000-65535
release id: 501
idpool is 98-101,113-119,300,400-501,600-649,651-700,1000-1050,2000-65535
release id: 399
idpool is 98-101,113-119,300,399-501,600-649,651-700,1000-1050,2000-65535
release id: 800
idpool is 98-101,113-119,300,399-501,600-649,651-700,800,1000-1050,2000-65535
release id: 8000
8000 has been in pool
idpool is 98-101,113-119,300,399-501,600-649,651-700,800,1000-1050,2000-65535
release id: 1
idpool is 1,98-101,113-119,300,399-501,600-649,651-700,800,1000-1050,2000-65535

Process finished with exit code 0

测试代码:

package idpool;

/**
 * @author shaozhugui
 * @date 2021/2/27
 */
public class IdTest {
    public static void main(String[] args) {
        IdPooImpl idPoo = new IdPooImpl("2-5,20,30,40-45,105-120,300,400-500,600-700,1000-1050,2000-65535");

        int[] getIds = {2,4,20,41,43,105,120,650,350};
        for (int id : getIds) {
            try {
                System.out.println("idpool is " + idPoo.getIdPool());
                System.out.println("get Id for targetId:" + idPoo.getId(id));
            } catch (IllegalAccessException e) {
                break;
            }
        }

        for (int i = 1; i < 15; i++) {
            try {
                System.out.println("idpool is " + idPoo.getIdPool());
                System.out.println("get Id for default: " + idPoo.getId());
            } catch (IllegalAccessException e) {
                break;
            }
        }

        int[] releaseIds = {100,99,98,101,101,501,399,800,8000,1};
        for (int releaseId : releaseIds) {
            System.out.println("release id: " + releaseId);
            idPoo.releaseId(releaseId);
            System.out.println("idpool is " + idPoo.getIdPool());
        }
    }
}

package idpool;

/**
 * @author shaozhugui
 * @date 2021/2/27
 */
public class IdPooImpl {
    private String idPool = "1-65535";//"2-10,20,30,40-100,105-200,300-65535", ""

    private static final String COMMA = ",";
    private static final String CONN = "-";

    public IdPooImpl(String idPool) {
        this.idPool = idPool;
    }

    //释放id
    public boolean releaseId(int targetId) {
        if (targetId <= 0) {
            return false;
        }
        if("".equals(idPool)) {
            idPool = String.valueOf(targetId);
            return true;
        }
        String[] idList = idPool.split(COMMA);
        //二分法查找目标id所在的区间(变异二分法)
        int index = getIndexByDichotomySearch(idList, targetId);
        int start = getStart(idList[index]);
        int end = getEnd(idList[index]);
        //System.out.println("index is " + index + "; start = " + start + "; end = " + end);
        if (targetId >= start && targetId <= end) {
            System.out.println(targetId + " has been in pool");
            return true;
        }
        index = targetId > end ? index : index - 1;
        // 之后,targetId必然位于index与index+1之间
        String temp = "";
        if (index >=0 && index + 1 <= idList.length - 1) {
            String pre = idList[index];
            String next = idList[index + 1];
            temp = getTempStr(pre, next, targetId);
        } else if (index == -1) {
            String next = idList[index + 1];
            temp = getTempStr(next, targetId);
        } else if (index == idList.length - 1){
            String pre = idList[index];
            temp = getTempStr2(pre, targetId);
        }
        StringBuilder builder = new StringBuilder();
        for(int i = 0; i < index; i++) {
            builder.append(idList[i]).append(COMMA);
        }
        builder.append(temp);

        if (index < idList.length - 2) {
            builder.append(COMMA);
            for(int i = index + 2; i < idList.length - 1; i++) {
                builder.append(idList[i]).append(COMMA);
            }
            builder.append(idList[idList.length - 1]);
        }

        idPool = builder.toString();
        return true;
    }

    private String getTempStr2(String pre, int targetId) {
        int preStart = getStart(pre);
        int preEnd = getEnd(pre);
        String temp;
        if (targetId - preEnd != 1) {
            temp = pre + COMMA + targetId;
        } else {
            temp = preStart + CONN + targetId;
        }
        return temp;
    }

    private String getTempStr(String next, int targetId) {
        int nextStart = getStart(next);
        int nextEnd = getEnd(next);
        String temp;
        if (nextStart - targetId != 1) {
            temp = targetId + COMMA + next;
        } else {
            temp = targetId + CONN + nextEnd;
        }
        return temp;
    }

    private String getTempStr(String pre, String next, int targetId) {
        int preStart = getStart(pre);
        int preEnd = getEnd(pre);
        int nextStart = getStart(next);
        int nextEnd = getEnd(next);
        String temp;
        if (targetId - preEnd != 1 && nextStart - targetId != 1) {
            temp = pre + COMMA + targetId + COMMA + next;
        } else if (targetId - preEnd == 1 && nextStart - targetId != 1) {
            temp = preStart + CONN + targetId + COMMA + next;
        } else if (targetId - preEnd != 1 && nextStart - targetId == 1) {
            temp = pre + COMMA + targetId + CONN + nextEnd;
        } else {
            temp = preStart + CONN + nextEnd;
        }
        return temp;
    }


    //指定Id分配
    public int getId(int targetId) throws IllegalAccessException {
        if("".equals(idPool)) {
            System.out.println("id pool is empty.");
            throw new IllegalAccessException();
        }

        String[] idList = idPool.split(COMMA);
        //二分法查找目标id所在的区间(变异二分法)
        int index = getIndexByDichotomySearch(idList, targetId);

        // 到对应的区间获取资源
        AllocateResult result = allocateId(targetId, idList[index]);
        if (!result.isResult()) {
            System.out.println("id is not in pool");
            throw new IllegalAccessException();
        }
        StringBuilder builder = new StringBuilder();
        for(int i = 0; i < index; i++) {
            builder.append(idList[i]).append(COMMA);
        }
        if (!"".equals(result.getStr())) {
            builder.append(result.getStr()).append(COMMA);
        }
        for(int i = index + 1; i < idList.length - 1; i++) {
            builder.append(idList[i]).append(COMMA);
        }
        builder.append(idList[idList.length - 1]);
        idPool = builder.toString();
        return targetId;
    }

    private AllocateResult allocateId(int targetId, String s) {
        AllocateResult result = new AllocateResult();
        if (!s.contains(CONN)) {
            if (targetId == Integer.parseInt(s)) {
                result.setResult(true);
                result.setStr("");
            }
            return result;
        }
        int start = getStart(s);
        int end = getEnd(s);
        if (start > targetId || end < targetId) {
            return result;
        }

        result.setResult(true);
        if (start == targetId) {
            start++;
            if (start == end) {
                result.setStr(String.valueOf(end));
            } else {
                result.setStr(start + CONN + end);
            }
            return result;
        }
        if (end == targetId) {
            end--;
            if (start == end) {
                result.setStr(String.valueOf(end));
            } else {
                result.setStr(start + CONN + end);
            }
            return result;
        }
        int tempStart = targetId + 1;
        int tempEnd = targetId - 1;
        StringBuilder builder = new StringBuilder();
        builder.append(start);
        if (start != tempEnd) {
            builder.append(CONN).append(tempEnd);
        }
        builder.append(COMMA);
        if (tempStart != end) {
            builder.append(tempStart).append(CONN);
        }
        builder.append(end);
        result.setStr(builder.toString());
        return result;
    }

    private int getIndexByDichotomySearch(String[] idList, int targetId) {
        int start = 0;
        int end = idList.length - 1;
        int middleByEnd = 0;
        while (start <= end) {
            middleByEnd = (start + end) / 2;
            int temp = getEnd(idList[middleByEnd]);
            if (temp == targetId) {
                return middleByEnd;
            } else if (temp < targetId) {
                start = middleByEnd + 1;
            } else {
                end = middleByEnd - 1;
            }
        }

        int start1 = 0;
        int end1 = idList.length - 1;
        int middleByStart = 0;
        while (start1 <= end1) {
            middleByStart = (start1 + end1) / 2;
            int temp = getStart(idList[middleByStart]);
            if (temp == targetId) {
                return middleByStart;
            } else if(temp < targetId) {
                start1 = middleByStart + 1;
            } else {
                end1 = middleByStart - 1;
            }
        }
        // targetId必然比middleByStart位置的开头小,必然比middleByEnd的结尾大;
        // System.out.println("startIndex is " + middleByStart + "; endIndex is " + middleByEnd);
        return (middleByEnd + middleByStart) / 2 ;
    }

    private int getEnd(String s) {
        if (!s.contains(CONN)) {
            return Integer.parseInt(s);
        }
        String[] strs = s.split(CONN);
        return Integer.parseInt(strs[1]);
    }

    private int getStart(String s) {
        String[] strs = s.split(CONN);
        return Integer.parseInt(strs[0]);
    }


    //自由分配Id,选取最小值
    public int getId() throws IllegalAccessException {
        if("".equals(idPool)) {
            System.out.println("id pool is empty.");
            throw new IllegalAccessException();
        }

        String[] idList = idPool.split(COMMA);
        String allocateStr = idList[0];
        String[] allocateStrs = allocateStr.split(CONN);
        int result = Integer.parseInt(allocateStrs[0]);
        String tempStr = "";
        if (allocateStrs.length != 1) {
            tempStr = allocateStrs[1];
            int end = Integer.parseInt(tempStr);
            if (end != result + 1) {
                tempStr = (result + 1) + CONN + tempStr;
            }
        }

        if (idList.length == 1) {
            idPool = tempStr;
            return result;
        }
        StringBuilder builder;
        if ("".equals(tempStr)) {
            builder = new StringBuilder(idList[1]);
        } else {
            builder = new StringBuilder(tempStr).append(COMMA).append(idList[1]);
        }

        for(int j = 2; j < idList.length; j++) {
            builder.append(COMMA).append(idList[j]);
        }

        idPool = builder.toString();
        return result;
    }

    public String getIdPool(){
        return idPool;
    }
}


class AllocateResult {
    private boolean result;
    private String str;

    public boolean isResult() {
        return result;
    }

    public void setResult(boolean result) {
        this.result = result;
    }

    public String getStr() {
        return str;
    }

    public void setStr(String str) {
        this.str = str;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值