【Java作业】树形结构(组合)

分类专栏:
Java作业

文章标签:
Java
Java基础
原创

一、实验内容

1.问题  

菜单是一对多的树型结构。如下图是早期TurboC2.0的菜单结构:

根据给定的菜单数据文件:

File Load F3 Pick Alt-F3 New Save F2 Write to Diretory Change Dir Os Shell Quit Alx-X Edit Run Program reset Ctrl-F2 Go To cursor F4 Trace into F7 Step over F8 User Screen Alt-F5 Compile Compile to OBJ Make EXE file Link EXE file Build all Primary C file Get info Project Project name Break make on Auto dependencies Clear project Remove messages Break/Watch Add watch Ctrl-F7 Delete watch Edit watch Remove all watches Toggle breakpoint Ctrl-F8 Clear all breakpoints View next breakpoint Debug Evalute Ctrl-F4 Call stack Ctrl-F3 Find function Refresh display Display swapping Source debugging Options Compiler Model Defines Code generation Optimization Source Errors Names Linker Map file Initialize segments Default library Warn duplicate symbols Stack warning Case-sensitive link Environment Message Tracking Keep messages Config auto save Edit auto save Backup files Tab Size Zoomed windows Screen size Directories Include directories Library directories Output directories Turbo C directory Pick file name Current pick file Arguments Save options Retrieve options

3.要求

①读菜单数据文件,建立如上图的树的实例;

②用宽度遍历,输出菜单标题验证。

③请看菜单项目.ppt后完成。

注:该资料只有一个页面是跟Java有关,部分是按C写的材料,请结合Java语言来完成。

二、程序设计

1.UML图

在这里插入图片描述

2.设计思路

①存储结构MenuItem、MenuAnalyseStru

MenuIterm类用来存储菜单选项,其内有私有属性menuTitle(存储菜单选项内容)和私有属性menu(存储子节点的list),对外提供相应的get,set方法,同时提供print方法,用来递归打印自身以及子节点 在这里插入图片描述

MenuAnalyseStru类用来存储每层的菜单信息,其内用属性preBlankCount(存储前导空格数)和list(存储改成的菜单选项),对外提供相应的get,set方法,同时提供add方法,用来添加该层的菜单选项 在这里插入图片描述

②文件读取工具类MyFileReader

为了符合设计模式中的单一职责原则,将文件读取、计算前置空格和处理存储等操作进行抽象成单独的类,降低程序的复杂度。 利用加载器中的方法获取该程序的运行路径,在利用该路径得到相应的文件输入流,逐行读入返回String列表。 在这里插入图片描述

③正则表达式封装类RegexUtil

此类为工具类,主要根据传入的正则表达式字符串返回匹配到的第一个字符串(即前置空格) 主要是封装了相应操作(利用jdk中的Pattern和Matcher类读取我们所需要的字符串) 在这里插入图片描述

④处理类Handler

此类主要功能是对从文件读取到的字符串list进行处理,将其存储在我们设定好的存储结构MenuItem、MenuAnalyseStru中。 具体思路如下: 在这里插入图片描述

⑤测试类

之前将各个操作抽象成单独类,所以测试类只需按次序调用相应的类中的方法即可 在这里插入图片描述

三、代码详情

1.菜单项MenuItem类

package com.dreamchaser.work4;

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

/**
 * 存储每项信息
 */
class MenuItem{

    /**
     * 菜单内容
     */
    private String menuTitle;
    /**
     * 子菜单
     */
    private List<MenuItem> menu=new ArrayList<>(3);

    public MenuItem(String menuTitle) {
        this.menuTitle = menuTitle;
    }

    /**
     * 打印自身及以下结点
     */
    public void print(){
        System.out.println(menuTitle);
        /**
         * 递归打印子节点
         */
        for (MenuItem menuItem:menu){
            menuItem.print();
        }
    }

    public String getMenuTitle() {
        return menuTitle;
    }

    public void setMenuTitle(String menuTitle) {
        menuTitle = menuTitle;
    }

    public List<MenuItem> getMenu() {
        return menu;
    }

    public void setMenu(List<MenuItem> menu) {
        this.menu = menu;
    }
}

2.菜单层MenuAnalyseStru类

package com.dreamchaser.work4;

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

/**
 * 存储每层信息
 */
public class MenuAnalyseStru {
    /**
     * 前导空格数
     */
    private Integer preBlankCount=0;
    /**
     * 同层的菜单项
     */
    private LinkedList<MenuItem> list=new LinkedList<>();

    public MenuAnalyseStru(Integer preBlankCount) {
        this.preBlankCount = preBlankCount;
    }

    /**
     * 添加一个菜单项
     * @param menuItem
     */
    public void add(MenuItem menuItem){
        list.add(menuItem);
    }

    public Integer getPreBlankCount() {
        return preBlankCount;
    }

    public LinkedList<MenuItem> getList() {
        return list;
    }
}

3.文件读取工具类MyFileReader

package com.dreamchaser.work4;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 文件读取类
 */
public class MyFileReader {
    /**
     * 读取项目文件,以行字符串list返回
     * @param name
     * @return
     */
    public static List<String> readFile(String name){
        List<String> list=new ArrayList<>(20);
        //获取文件加载时的绝对路径
        String filePath=MyFileReader.class.getClassLoader().getResource(name).getFile();
        File file=new File(filePath);
        if (file.exists()&&file.isFile()){
            BufferedReader reader=null;
            try {
                reader=new BufferedReader(new FileReader(file));
                String line=null;
//                //前置空格
                while ((line=reader.readLine())!=null){
                    list.add(line);
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                try {
                    //最后将输入流关闭
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
        return list;
    }
}

4.正则表达式封装类RegexUtil

package com.dreamchaser.work4;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexUtil {
    /**
     * 返回单个字符串,若匹配到多个的话就返回第一个,方法与getSubUtil一样
     * @param soap
     * @param rgex
     * @return
     */
    public static String getSubUtilSimple(String soap,String rgex){
        Pattern pattern = Pattern.compile(rgex);// 匹配的模式
        Matcher m = pattern.matcher(soap);
        int i=0;
        while(m.find()){
            return m.group(0);
        }
        return "";
    }
}

5.处理类Handler

package com.dreamchaser.work4;

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

/**
 * 用于处理读取文件后的list,将其处理成根目录返回
 */
public class Handler {
    public static List<MenuAnalyseStru> handle(List<String> strings){
        List<MenuAnalyseStru> menuAnalyseStrus=new ArrayList<>(5);
        /**
         * 表示层数
         */
        Integer layer=0;
        /**
         * 前置空格数
         */
        Integer pre=0;
        for (String s:strings){
            //通过正则表达式匹配相应的前置空格,然后统计长度赋值给pre
            pre=RegexUtil.getSubUtilSimple(s,"^ +").length();
            //创建相应的菜单选项
            MenuItem menuItem=new MenuItem(s);

            if (menuAnalyseStrus.size()==pre/4){
                //当前层如果没有创建对象则先创建对象
                MenuAnalyseStru menuAnalyseStru=new MenuAnalyseStru(pre);
                menuAnalyseStru.add(menuItem);
                menuAnalyseStrus.add(menuAnalyseStru);
            }else {
                menuAnalyseStrus.get(pre/4).add(menuItem);
            }
            //如果不是第一层的话,则要把子节点加入父节点中
            if (pre!=0){
                //获取上一层的最后一个,即父节点
                MenuItem father=menuAnalyseStrus.get(pre/4-1).getList().getLast();
                //再将其加入父节点的子节点中
                father.getMenu().add(menuItem);
            }
        }
        return menuAnalyseStrus;
    }


}

6.测试类Test

package com.dreamchaser.work4;


import java.util.List;

/**
 * 测试类
 */
public class Test {
    public static void main(String[] args) {
        //读取文件
        List<String> list=MyFileReader.readFile("TurboC.txt");
        //处理数据
        List<MenuAnalyseStru> strus=Handler.handle(list);
        //打印输出,只需获取顶层的菜单选项遍历即可
        for (MenuItem m:strus.get(0).getList()){
            m.print();
        }
    }
}

四、运行结果(测试结果)

在这里插入图片描述

在这里插入图片描述

五、收获心得

主要花时间的地方在于handler将数组读取进我们设计好的数据结构中,那部分逻辑有点绕,一开始想复杂了,后来发现其实可以挺简单的。 还有就是如何读取前置空格,那时候我只知道可以用正则表达式去做,至于怎么做还是有点模糊的(之前没怎么遇到就没什么印象了),后来去翻了别人的博客才渐渐掌握。 除此之外,文件路径的读取也是一个难点,这个路径一般只能用绝对路径(在maven项目中,把资源文件放在resouces目录下),用相对路径会报文件未找到的异常,所以如何根据程序运行环境去读取文件的绝对路径也是一个难点。

  • 作者:金昊霖
  • 发表时间:2020-7-04
  • 版权声明:自由转载-非商用-非衍生-保留署名(创意共享3.0许可证)
  • 评论

    留言