属性字符串解析

连续的K=V的字符串,每个K=V之间用","分隔,V中可嵌套K=V的连续字符串结构,例如“
key1=value1,key2=value2,key3=[key4=value4,key5=value5,key6=[key7=value7]],key8=value8
请编写如下函数,给定字符串,输出嵌套结构的HashMap
HashMap parse(String input) {
    ...
}

附:生成连续字符串结构的代码,通过调整嵌套最大层级和size数组大小,控制生成的字符串大小
    static int count=0;//key计数器
    
    /**
     * 生成测试字符串
     * @param ceng 嵌套最大层级
     * @return
     */
    public static String createString(int ceng){
        if(ceng>5000)return "";
        String[] size=new String[10000];   //每层结构一共存在键值对的数量
        for(int i=0;i0){ //有层级
                String key="key"+(++count);
                size[i]=key+"=["+createString(++ceng)+"]";
            }else{
                String key="key"+(++count);
                size[i]=key+"=value"+count;
            }
        }
        return  String.join(",",size);
    }

解法1   

 只处理当前字符串第一个嵌套体位置的前面普通键值对,将第一个嵌套体内容递归处理,将第一个嵌套体后面剩余的字符串递归处理

 HashMap parse(String input){
        HashMap hashMap=new HashMap<>();
        char[] chars=input.toCharArray();
        int keyStart=0;
        int start=-1;//[位置
        int end=-1;//]位置
        int stack=0;//栈计数器
        for (int i=0;i0;j--){
                        if(chars[j]==','){
                            keyStart=j+1;
                            break;
                        }
                    }
                }
            }else if(chars[i]==']'){
                if(stack==1){
                    end=i;
                    break;
                }
                stack--;
            }
        }
        String[] keyValueArray=new String[0];
        if(start>-1){
            //这里处理前半截
            if(keyStart-1>=0){
                keyValueArray=input.substring(0,keyStart-1).split(",");
            }
            //发现有[]  ,  递归处理
            String temp=input.substring(start,end);
            hashMap.put(input.substring(keyStart,start-2),parse(temp));
            //发现后面还有东西
            if(end

解法2

   依次将当前字符串中所有普通键值对进行处理,将所有遇到的嵌套体内容递归处理,减少递归次数,但逻辑更加复杂

    HashMap parse(String input) {
        HashMap hashMap = new HashMap<>();
        char[] chars = input.toCharArray();
        int kvStart = 0;//记录当前键值对的开始字段
        int keyStart = 0;
        int start = -1;//[位置
        int end = -1;//]位置
        int stack = 0;//栈计数器
        for (int i = 0; i < chars.length; i++) {
            if (chars[i] == ',' && stack == 0) {//stack=0,表示不在嵌套结构内,作为普通的键值对解析
                String[] array = input.substring(kvStart, i).split("=");
                hashMap.put(array[0], array[1]);
                kvStart = i + 1;
            }else if(i==chars.length-1&&chars[i] != ']'){//尾部处理:如果是普通的字符串,尾部肯定不是],作为普通键值对
                String[] array = input.substring(kvStart, i+1).split("=");
                hashMap.put(array[0], array[1]);
            }else if (chars[i] == '[') {
                stack++;
                if (stack == 1) {//第一次嵌套开始
                    start = i + 1;//嵌套内容起始位置
                    for (int j = i; j > 0; j--) {
                        if (chars[j] == ',') {
                            keyStart = j + 1;//当前嵌套体的key起始字符位置
                            break;
                        }
                    }
                }
            } else if (chars[i] == ']') { //第一次嵌套结束
                if (stack == 1) {
                    end = i;
                    hashMap.put(input.substring(keyStart, start - 2), parse(input.substring(start, end)));
                    i++;//跳过逗号
                    kvStart = i + 1;//下个键值对的起始位置
                }
                stack--;
            }
        }
        return hashMap;
    }

解法3 

不用递归,但利用递归的思想,将递归的参数封装,并构建成队列,进行循环操作,模拟递归。

这样的好处是,递归方法栈容量有限。当递归时方法栈的参数越多,占用栈内存越大,总栈帧数量越小,自己定义的队列不受此限制

HashMap parse(String input) {
        //根
        HashMap root = new HashMap<>();
        //初始化列表
        List>> nodeList=new ArrayList<>();
        nodeList.add( new Pair<>(input,root));
        //处理列表元素
        while (nodeList.size()>0){
            //每次取出第一个元素
            Pair> node=nodeList.get(0);
            nodeList.remove(0);
            HashMap hashMap=node.getValue();
            String temp=node.getKey();
            char[] chars = temp.toCharArray();
            int keyStart = 0,start = -1,end = -1,stackCount = 0;//记录当前键值对的开始字段
            for (int i = 0; i < chars.length; i++) {
                if (chars[i] == ',' && stackCount == 0) {//stack=0,表示不在嵌套结构内,作为普通的键值对解析
                    String[] array = temp.substring(keyStart, i).split("=");
                    hashMap.put(array[0], array[1]);
                    keyStart = i + 1;
                }else if(i==chars.length-1&&chars[i] != ']'){//尾部处理:如果是普通的字符串,尾部肯定不是],作为普通键值对
                    String[] array = temp.substring(keyStart, i+1).split("=");
                    hashMap.put(array[0], array[1]);
                }else if (chars[i] == '[') {
                    stackCount++;
                    if (stackCount == 1) {//第一次嵌套开始
                        start = i + 1;//嵌套内容起始位置
                        for (int j = i; j > 0; j--) {
                            if (chars[j] == ',') {
                                keyStart = j + 1;//当前嵌套体的key起始字符位置
                                break;
                            }
                        }
                    }
                } else if (chars[i] == ']') { //第一次嵌套结束
                    if (stackCount == 1) {
                        end = i;
                        HashMap child = new HashMap<>();
                        hashMap.put(temp.substring(keyStart, start - 2), child);//先存放嵌套对象的地址
                        nodeList.add(new Pair<>(temp.substring(start, end),child));//加入堆栈等待处理
                        i++;//跳过逗号
                        keyStart = i + 1;//下个键值对的起始位置
                    }
                    stackCount--;
                }
            }
        }
        return root;
    }

测试结果:

测试字符串长度:617534
解法1时间:31,解法2时间:32,解法3时间:95

测试字符串长度:617535
解法1时间:33,解法2时间:46,解法3时间:94

测试字符串长度:6817510
解法1时间:188,解法2时间:173,解法3时间:236

测试字符串长度:6817510
解法1时间:206,解法2时间:141,解法3时间:255

测试字符串长度:6817511
解法1时间:204,解法2时间:157,解法3时间:236

测试字符串长度:74577486
解法1时间:3582,解法2时间:3846,解法3时间:2293

测试字符串长度:74577486
解法1时间:2387,解法2时间:3840,解法3时间:2685

测试字符串长度:74577486
解法1时间:3354,解法2时间:2492,解法3时间:2312

测试字符串长度:74577486
解法1时间:2970,解法2时间:3272,解法3时间:2727

数据量小的时候发现递归存在很大的优势,结构简单,执行快

数据量大的时候非递归显露出较大的优势,结构复杂,执行快

总结:实际情况下可以根据设置长度阈值,动态选择相应的解析算法

你可能感兴趣的:(算法,java,开发语言)