NodeVO.java:


import lombok.Getter;
import lombok.Setter;

import java.io.Serializable;
import java.util.List;

@Getter
@Setter
public class NodeVO implements Serializable {

    private String key;
    private String parentKey;
    private NodeVO parent;

    private NodeVO prev;

    private String jing;
    private String name;
    private List<NodeVO> children;
}

 

MarkdownUtils.java:
import java.util.*;

public class MarkdownUtils {

    public static void print(NodeVO nodeVo,String padding) {
        if( nodeVo == null ){
            return;
        }
        System.out.println( padding + nodeVo.getName() );
        List<NodeVO> children = nodeVo.getChildren();
        if( children == null || children.size() == 0 ){
            return;
        }
        for( NodeVO child:children ){
            print( child,padding + "\t" );
        }
    }

    /**
     *
     * @param title
     * @return 数组中0角标处的元素是 jing号,数组中1角标处的元素是name
     */
    public static String[] extractJingAndNameFromTitle( String title ){
        String name = title.replaceFirst("[#]+", "");
        String jing = title.replaceFirst(name, "");
        String[] result = new String[2];
        result[ 0 ] = jing;
        result[ 1 ] = name;
        return result;
    }

    public static List<NodeVO> markdownTitles2JsonTree(List<String> titles ){
        if( titles == null || titles.size()==0 ){
            return new ArrayList<>( 0 );
        }
        int size = titles.size();

        // init  map_key_nodeVO
        Map<String, NodeVO> map_key_nodeVO = new HashMap<>();
        NodeVO node_curr = null;
        NodeVO node_prev = null;
        for( int i=0;i<size;i++ ){
            node_prev = node_curr;

            String title = titles.get(i);
            String[] result = MarkdownUtils.extractJingAndNameFromTitle(title);
            String jing = result[0];
            String name = result[1];

            node_curr = new NodeVO();
            node_curr.setPrev( node_prev );
            node_curr.setJing( jing );
            node_curr.setName( name );
            String key = i + jing + name;
            node_curr.setKey( key );
            map_key_nodeVO.put( key,node_curr );
        }

        String key_curr = null;
        String key_prev = null;
        // # xxx
        // ## xxx_1
        // ### xxx_1_1
        // # yyy

        for( int i=0;i<size;i++ ){
            key_prev = key_curr;

            String title = titles.get(i);
            String[] result = MarkdownUtils.extractJingAndNameFromTitle(title);
            String jing = result[0];
            String name = result[1];
            key_curr = i + jing + name;

            handle( key_curr,key_prev,map_key_nodeVO );
        }

        List<NodeVO> nodeVOs_top = new ArrayList<>();
        Set<String> keys = map_key_nodeVO.keySet();
        for( String key:keys ){
            NodeVO nodeVO = map_key_nodeVO.get(key);
            if( nodeVO.getParent() == null ){
                nodeVOs_top.add( nodeVO );
            }
        }
        return nodeVOs_top;
    }

    private static void handle(String key_curr, String key_prev, Map<String, NodeVO> map_key_nodeVO) {
        if( key_prev == null ){
            return;
        }
        // 比较当前两个节点的  #个数
        NodeVO node_curr = map_key_nodeVO.get(key_curr);
        NodeVO node_prev = map_key_nodeVO.get(key_prev);

        String jing_curr = node_curr.getJing();
        String jing_prev = node_prev.getJing();

        if(jing_curr.length() == jing_prev.length() ){
            //  当前标题 和 前一个标题 是 兄弟标题,有同样的父标题,或者都没有父标题
            NodeVO parent = node_prev.getParent();
            if( parent == null ){
                // 都没有父标题
            }else {
                // 有同样的父标题
                node_curr.setParent( parent );
                parent.getChildren().add( node_curr );
            }
        }else if( jing_curr.length() < jing_prev.length() ){
            // 将当前标题 和 前一个标题 的 前一个标题 继续做比较( 递归 )
            NodeVO node_prev_prev = node_prev.getPrev();
            if( node_prev_prev == null ){
                handle( key_curr,null,map_key_nodeVO );
            }else {
                handle( key_curr,node_prev_prev.getKey(),map_key_nodeVO );
            }
        }else if( jing_curr.length() > jing_prev.length() ){
            // 当前标题 是 前一个标题 的子标题
            node_curr.setParent( node_prev );
            List<NodeVO> children = node_prev.getChildren();
            if( children == null ){
                children = new ArrayList<>();
                node_prev.setChildren( children );
            }
            children.add( node_curr );
        }
    }
}

 

 测试代码:



import java.util.ArrayList;
import java.util.List;

public class Test {


    public static void main(String[] args) {
        List<String> titles = new ArrayList<>();
        titles.add( "#大洲" );
        titles.add( "##南美洲" );
        titles.add( "###巴西" );
        titles.add( "####圣保罗" );
        titles.add( "####巴西利亚" );
        titles.add( "####里约热内卢" );
        titles.add( "###智利" );
        titles.add( "####圣地亚哥" );
        titles.add( "###阿根廷" );
        titles.add( "###布宜诺斯艾利斯" );
        titles.add( "###哥伦比亚" );
        titles.add( "####圣菲波哥大" );
        titles.add( "###委内瑞拉" );
        titles.add( "####加拉加斯" );
        titles.add( "###秘鲁" );
        titles.add( "##北美洲" );
        titles.add( "###美国" );
        titles.add( "####华盛顿" );
        titles.add( "####纽约" );
        titles.add( "####旧金山" );
        titles.add( "####芝加哥" );
        titles.add( "####阿拉斯加" );
        titles.add( "###加拿大" );
        titles.add( "####渥太华" );
        titles.add( "####魁北克" );
        titles.add( "##亚洲" );
        titles.add( "###中国" );
        titles.add( "####北京" );
        titles.add( "####上海" );
        titles.add( "####广州" );
        titles.add( "####深圳" );
        titles.add( "###朝鲜" );
        titles.add( "###日本" );
        titles.add( "###韩国" );
        titles.add( "###泰国" );
        titles.add( "##欧洲" );
        titles.add( "###英国" );
        titles.add( "####伦敦" );
        titles.add( "####利物浦" );
        titles.add( "####约克" );
        titles.add( "####牛津" );
        titles.add( "####罗切斯特" );
        titles.add( "###德国" );
        titles.add( "####柏林" );
        titles.add( "####慕尼黑" );
        titles.add( "####汉堡" );
        titles.add( "####巴伐利亚" );
        titles.add( "####勃兰登堡" );
        titles.add( "####布莱梅" );
        titles.add( "###法国" );
        titles.add( "####巴黎" );
        titles.add( "####马赛" );
        titles.add( "####里昂" );
        titles.add( "####尼斯" );
        titles.add( "###俄罗斯" );
        titles.add( "####莫斯科" );
        titles.add( "####斯大林格勒" );
        titles.add( "####圣彼得堡" );
        titles.add( "####叶卡捷琳堡" );
        titles.add( "###西班牙" );
        titles.add( "###丹麦" );
        titles.add( "####哥本哈根" );
        titles.add( "###挪威" );
        titles.add( "####奥斯陆" );
        titles.add( "##大洋洲" );
        titles.add( "###澳大利亚" );
        titles.add( "###新西兰" );
        titles.add( "###斐济" );
        titles.add( "##非洲" );
        titles.add( "###南非" );
        titles.add( "###刚果" );
        titles.add( "###阿尔及利亚" );
        titles.add( "##南极洲" );

        List<NodeVO> nodeVos = MarkdownUtils.markdownTitles2JsonTree( titles );
        for( NodeVO nodeVo:nodeVos ){
            MarkdownUtils.print( nodeVo,"" );
        }
    }
}

 

转换成json树结构之后的输出:


大洲
	南美洲
		巴西
			圣保罗
			巴西利亚
			里约热内卢
		智利
			圣地亚哥
		阿根廷
		布宜诺斯艾利斯
		哥伦比亚
			圣菲波哥大
		委内瑞拉
			加拉加斯
		秘鲁
	北美洲
		美国
			华盛顿
			纽约
			旧金山
			芝加哥
			阿拉斯加
		加拿大
			渥太华
			魁北克
	亚洲
		中国
			北京
			上海
			广州
			深圳
		朝鲜
		日本
		韩国
		泰国
	欧洲
		英国
			伦敦
			利物浦
			约克
			牛津
			罗切斯特
		德国
			柏林
			慕尼黑
			汉堡
			巴伐利亚
			勃兰登堡
			布莱梅
		法国
			巴黎
			马赛
			里昂
			尼斯
		俄罗斯
			莫斯科
			斯大林格勒
			圣彼得堡
			叶卡捷琳堡
		西班牙
		丹麦
			哥本哈根
		挪威
			奥斯陆
	大洋洲
		澳大利亚
		新西兰
		斐济
	非洲
		南非
		刚果
		阿尔及利亚
	南极洲


09-05 05:17