一、项目介绍

  实现一个帮助进行地铁出行路线规划的命令行程序。

  github链接:https://github.com/blakeyoungc/subway

  主要需求:1、地铁线路图信息的导入

         2、查询指定地铁线经过的站点 

       3、计算从出发到目的站点之间的最短(经过的站点数最少)路线,并输出经过的站点的个数和路径(包括出发与目的站点)。

采用https://map.bjsubway.com/上的北京地铁线路图:

 二、需求实现

  

主要模块介绍:

序号模块名功能对应java类
1主模块(main函数)对输入参数的控制Main.java
2文件输入输出模块txt文件的读写Manager.java
3求最短路径(dij算法)实现两个站点的最短路线查找Subway.java

Main函数中区分输入不同参数时执行的操作。

public class Main {
    public static void main(String[] args)  {
        if(args.length==2||args.length==6||args.length==7) {                        //通过case分别处理不同参数输入时的操作
         switch(args[0]) {
         case "-map":
            Manager.loadfile();
            break;
         case "-a":
            Manager.loadfile();
            Manager.searchstations(args[1]);
            break;
         case "-b":
            Manager.loadfile();
            Route results=Subway.getRoutine(args[1],args[2]);
            Manager.outputroute(results);
            break;
         }
        }
        else
            System.out.println("格式错误!");
    }
}

车站及线路类以及其方法的设计

车站类:

import java.util.*;

public class Station {
    private String stationName;                                               //站点名
    private String lineName;                                                  //所在路线名
    private List<Station> linkStations = new ArrayList<>();                   //存储相邻的站点
    public Station(String stationName){
        this.stationName = stationName;
    }

    public Station(String stationName,String lineName){
        this.stationName = stationName;
        this.lineName = lineName;
    }

    public String getStationName() {
        return stationName;
    }

    public void setStationName(String stationName) {
        this.stationName = stationName;
    }

    public String getLineName() {
        return lineName;
    }

    public void setLineName(String lineName) {
        this.lineName = lineName;
    }

    public List<Station> getLinkStations() {
        return linkStations;
    }

    public void setLinkStations(List<Station> linkStations) {
        this.linkStations = linkStations;
    }
}

线路类:

public class Route {
    private Station startstation;                                                 //起点
    private Station destination;                                                  //终点
    private List<Station>passedStations=new ArrayList<>();                        //存储最短路径经过的路线站点
    public Station getStartStation() {
        return startstation;
    }

    public void setStartStation(Station startstation) {
        this.startstation = startstation;
    }

    public Station getDestination() {
        return destination;
    }

    public void setDestination(Station destination) {
        this.destination = destination;
    }

    public List<Station> getPassedStations() {
        return passedStations;
    }

    public void setPassedStations(List<Station> passedStations) {
        this.passedStations = passedStations;
    }
}

需求1、地铁线路图信息的导入

程序启动时需要通过读取 -map 参数来获得对应的自定义地铁文件(命名为 subway.txt),从而得到地铁线路图的信息。

示例: java subway -map subway.txt 

通过Hashmap存储subway.txt中的线路站点信息,其中key存线路名,value存站点类的表,存到subwaymap中以供后面使用。

public  static HashMap<String, List<Station>> subwaymap = new HashMap<>();
    public static void loadfile() {
        File file=new File("C://subway.txt");
        try {
            InputStreamReader inputStreamReader=new InputStreamReader(new FileInputStream(file),"GBK");
            BufferedReader reader=new BufferedReader(inputStreamReader);
            String data=null;
            while((data=reader.readLine())!=null) {
            String[] linedata=data.trim().split(" ");                                              //按空格分割读取txt文件的数据
            List<Station>stations=new ArrayList<>();
            for(int i = 1;i<linedata.length;i++){
                Station station = new Station(linedata[i],linedata[0]);
                stations.add(station);
                }
                subwaymap.put(linedata[0], stations);                                              //按序存入subwaymap
            }
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

subway.txt中的北京地铁线路信息截图

需求2、查询指定地铁线经过的站点。

程序启动时需要通过读取 -a 参数来获得线路名,并输出该线路的站点到文件“station.txt”。

示例: -a 10号线 -map subway.txt -o station.txt 

把之前subwaymap中的数据根据用户输入的线路名进行筛选,有序读取该线路经过的站点并写入“station.txt”文件。

public static void searchstations(String routename) {
        File file=new File("C://station.txt");
        try {
            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(file),"GBK");
            BufferedWriter writer = new BufferedWriter(outputStreamWriter);
            List<Station> list = subwaymap.get(routename);
            writer.write(routename+" ");
                for(int i=0;i<list.size();i++){
                    writer.write(list.get(i).getStationName()+" ");
                        }
                writer.close();
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

   

查询10号线的所有站点,运行完成后station.txt结果截图 

需求3、查询两个站点的最短路径线路及换乘信息

程序启动时需要通过读取 -b 参数来获得输入的起点和终点,输出两个站点之间的最短路径经过的站点。

示例: -b 巴沟 土桥 -map subway.txt -o routine.txt 

查询从巴沟到土桥的最短路线站点及换乘线路。

通过Dijkstra算法求最短路径。

步骤如下:

   1:遍历所有节点找到未访问过的节点中累积权值(其实就是从源节点到当前节点的路径值和)最小的(设为A)。

   2:遍历该节点所有可达边(连通到目标节点B),如果节点A累积权值加可达边权值小于目标节点B自身的累积权值,则对目标节点B进行更新。

   3:将节点A设定为已访问。

   4:重复(1)直到所有节点均访问完毕。

public Route Dijkstra(Route routine) {
        Station s;
        Station S;
        HashMap<Station,Station> D = new HashMap<>();
        HashMap<Station,Station> prev = new HashMap<>();
        HashMap<Station,Integer> searched = new HashMap<>();
        HashMap<HashMap<Station,Station>,Integer> distance = new HashMap<>();
        for (String key :allStation.keySet()){
            s = allStation.get(key);
            searched.put(s,new Integer(0));
            if (!routine.getStartStation().equals(s)){
                if (routine.getStartStation().getLinkStations().contains(s)){
                    D = new HashMap<>();
                    D.put(routine.getStartStation(),s);
                    distance.put(D,new Integer(1));
                    prev.put(s,routine.getStartStation());
                }
                else{
                    D = new HashMap<>();
                    D.put(routine.getStartStation(),s);
                    distance.put(D,new Integer(Integer.MAX_VALUE));
                }
            }
            else{
                D = new HashMap<>();
                D.put(routine.getStartStation(),s);
                distance.put(D,new Integer(0));
            }
        }
        searched.put(routine.getStartStation(),1);
        while (true){
            S = FindMinDist(routine,distance,searched);
            if (S.getStationName().equals("-1"))
                break;
            searched.put(S,1);
            for (String key:allStation.keySet()){
                if (S.getLinkStations().contains(allStation.get(key))&&searched.get(allStation.get(key))==0){
                    if (distance.get(getFromtoFin(routine,S))+1<distance.get(getFromtoFin(routine,allStation.get(key)))){
                        distance.put(getFromtoFin(routine,allStation.get(key)),distance.get(getFromtoFin(routine,S))+1);
                        prev.put(allStation.get(key),S);
                    }
                }
            }
        }
        S = prev.get(routine.getDestination());
        while(!S.equals(routine.getStartStation())){
            routine.getPassedStations().add(0,S);
            S = prev.get(S);
        }
        routine.getPassedStations().add(0,routine.getStartStation());
        routine.getPassedStations().add(routine.getDestination());
        return routine;
    }

通过算法生成最短路径后,再进行换乘线路的判断,同时将数据输出到“routine.txt”文件中。

public static void outputroute(Route routine) {
        // TODO Auto-generated method stub
        File file=new File("C://routine.txt");
        try {
            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream(file),"GBK");
            BufferedWriter writer = new BufferedWriter(outputStreamWriter);
            writer.write(routine.getPassedStations().size()-1+" ");
            String linename="0";
             for (int i = 0; i < routine.getPassedStations().size();i++)
                {
                    writer.write(routine.getPassedStations().get(i).getStationName()+" ");
                    if (i<routine.getPassedStations().size()-1){
                        if (!routine.getPassedStations().get(i).getLineName().equals(routine.getPassedStations().get(i+1).getLineName())){      //换乘线路的判断
                            linename = routine.getPassedStations().get(i+1).getLineName();
                            if(linename!="0")
                            writer.write("换乘"+linename+"  ");
                        }
                    }
                }
            writer.close();
        }catch(IOException e){
            e.printStackTrace();
        }
    }

routine.txt中的最短路线站点及换乘信息

异常情况处理:

起点与终点相同:-b 土桥 土桥 -map subway.txt -o routine.txt

输出:起点与终点相同

 

起点或终点不存在: -b 123 456 -map subway.txt -o routine.txt

输出:起点或终点不存在

 

查询线路不存在: -a 1000号线 -map subway.txt -o station.txt

输出:线路不存在

 三、小结

本次个人作业中间出现了各种错误,其实还是对java代码的熟悉度不够,实验中多次出现Exception in thread "main" java.lang.NullPointerException的错误,主要因为数组为null了,没有初始化,浪费了很多时间找错,后续还要继续学习熟悉。

01-19 23:58