代码之家  ›  专栏  ›  技术社区  ›  Qing Li

在java中读取txt文件并将数据存储在哈希表中

  •  0
  • Qing Li  · 技术社区  · 7 年前

    我正在读取一个txt文件并将数据存储在哈希表中,但无法获得正确的输出。类似于此(部分)附加图像的txt文件 this is part of my data

    我想将列1和列2存储为哈希表中的键(字符串类型),将列3和列4存储为哈希表中的值(ArrayList类型)。 我的代码如下:

    private Hashtable<String, ArrayList<String[]>> readData() throws Exception {
        BufferedReader br = new BufferedReader (new FileReader("MyGridWorld.txt"));
        br.readLine();
    
        ArrayList<String[]> value = new ArrayList<String[]>();
        String[] probDes = new String[2];
        String key = "";
    
        //read file line by line
        String line = null;
        while ((line = br.readLine()) != null && !line.equals(";;")) {
            //System.out.println("line ="+line);
            String source;
            String action;
    
            //split by tab
            String [] splited = line.split("\\t"); 
            source = splited[0];
            action = splited[1];
            key = source+","+action;
    
            probDes[0] = splited[2];
            probDes[1] = splited[3];
    
            value.add(probDes);
            hashTableForWorld.put(key, value);
            System.out.println("hash table is like this:" +hashTableForWorld);
    
        }
    
        br.close();
        return  hashTableForWorld;
    
    
    }
    

    输出如下所示: it's a very long long line

    我想也许哈希表坏了,但我不知道为什么。谢谢你阅读我的问题。

    3 回复  |  直到 7 年前
        1
  •  2
  •   Xirema    7 年前

    我们需要确定的第一件事是你有一个 XY-Problem 因为“你需要做什么”和“你如何解决它”是完全不一致的。

    那么让我们回到原来的问题,试着找出我们 需要 第一

    尽我所能, source action 连接,因为它们表示数据结构的可查询“键”,并且 probability , destination reward 是数据结构中可查询的“结果”。因此,我们将首先创建对象来表示这两个概念:

    public class SourceAction implements Comparable<SourceAction>{
        public final String source;
        public final String action;
    
        public SourceAction() {
            this("", "");
        }
    
        public SourceAction(String source, String action) {
            this.source = source;
            this.action = action;
        }
    
        public int compareTo(SourceAction sa) {
            int comp = source.compareTo(sa.source);
            if(comp != 0) return comp;
            return action.compareto(sa.action);
        }
    
        public boolean equals(SourceAction sa) {
            return source.equals(sa.source) && action.equals(sa.action);
        }
    
        public String toString() {
            return source + ',' + action;
        }
    }
    
    public class Outcome {
        public String probability; //You can use double if you've written code to parse the probability
        public String destination;
        public String reward; //you can use double if you're written code to parse the reward
    
        public Outcome() {
            this("", "", "");
        }
    
        public Outcome(String probability, String destination, String reward) {
            this.probability = probability;
            this.destination = destination;
            this.reward = reward;
        }
    
        public boolean equals(Outcome o) {
            return probability.equals(o.probability) && destination.equals(o.destination) && reward.equals(o.reward);
    
        public String toString() {
            return probability + ',' + destination + ',' + reward;
        }
    }
    

    那么,给定这些对象,如果 SourceAction 似乎有 一对多 与的关系 Outcome 物体?我的建议是 Map<SourceAction, List<Outcome>> 表示此关系。

    private Map<SourceAction, List<Outcome>> readData() throws Exception {
    

    可以使用哈希表(在这种情况下, HashMap )包含这些对象,但我试图使代码尽可能简单,因此我们将坚持使用更通用的接口。

    然后,我们可以重用您在原始代码中使用的逻辑,将值插入到此数据结构中,只需进行一些调整。

    private Map<SourceAction, List<Outcome>> readData() {
        //We're using a try-with-resources block to eliminate the later call to close the reader
        try (BufferedReader br = new BufferedReader (new FileReader("MyGridWorld.txt"))) {
            br.readLine();//Skip the first line because it's just a header
    
            //I'm using a TreeMap because that makes the implementation simpler. If you absolutely 
            //need to use a HashMap, then make sure you implement a hash() function for SourceAction
            Map<SourceAction, List<Outcome>> dataStructure = new TreeMap<>();
    
            //read file line by line
            String line = null;
            while ((line = br.readLine()) != null && !line.equals(";;")) {
                //split by tab
                String [] splited = line.split("\\t"); 
                SourceAction sourceAction = new SourceAction(splited[0], splited[1]);
    
                Outcome outcome = new Outcome(splited[2], splited[3], splited[4]);
    
                if(dataStructure.contains(sourceAction)) {
                    //Entry already found; we're just going to add this outcome to the already
                    //existing list.
                    dataStructure.get(sourceAction).add(outcome);
                } else {
                    List<Outcome> outcomes = new ArrayList<>();
                    outcomes.add(outcome);
                    dataStructure.put(sourceAction, outcomes);
                }
            }
        } catch (IOException e) {//Do whatever, or rethrow the exception}
        return dataStructure;
    }
    

    然后,如果要查询与给定源+操作关联的所有结果,只需构造 SourceAction源操作 对象并在地图中查询它。

    Map<SourceAction, List<Outcome>> actionMap = readData();
    List<Outcome> outcomes = actionMap.get(new SourceAction("(1,1)", "Up"));
    assert(outcomes != null);
    assert(outcomes.size() == 3);
    assert(outcomes.get(0).equals(new Outcome("0.8", "(1,2)", "-0.04")));
    assert(outcomes.get(1).equals(new Outcome("0.1", "(2,1)", "-0.04")));
    assert(outcomes.get(2).equals(new Outcome("0.1", "(1,1)", "-0.04")));
    

    这将产生您想要的功能 需要 解决你的问题。

        2
  •  1
  •   RAZ_Muh_Taz    7 年前

    您应该更改添加到哈希表的逻辑,以检查您创建的键。如果键存在,则获取它映射到的数组的数组列表,并将数组添加到其中。当前您将覆盖数据。

    试试这个

    if(hashTableForWorld.containsKey(key))
    {
        value = hashTableForWorld.get(key);
        value.add(probDes);
        hashTableForWorld.put(key, value);
    }
    else
    {
        value = new ArrayList<String[]>();
        value.add(probDes);
        hashTableForWorld.put(key, value);
    }
    

    然后,要打印内容,请尝试以下方法

    for (Map.Entry<String, ArrayList<String[]>> entry : hashTableForWorld.entrySet()) {
        String key = entry.getKey();
        ArrayList<String[]> value = entry.getValue();
    
        System.out.println ("Key: " + key + " Value: ");
        for(int i = 0; i < value.size(); i++)
        {
            System.out.print("Array " + i + ": ");
            for(String val : value.get(i))
                System.out.print(val + " :: ")
            System.out.println();
        }
    }
    
        3
  •  1
  •   Zoe - Save the data dump 张群峰    6 年前

    Hashtable ArrayList (和其他集合)不复制键和值,因此存储的所有值都是相同的 probDes 刚开始分配的数组(请注意 String[] 以一种神秘的形式出现,你必须自己把它弄得漂亮,但你仍然可以看到它一直都是同一种神秘的东西)。
    可以肯定的是,您应该分配一个新的 probDes公司 对于循环内的每个元素。
    根据您的数据,您可以使用数组作为值在我看来,ArrayList没有真正的用途 这同样适用于 value ,遇到新的 key :

    private Hashtable<String, ArrayList<String[]>> readData() throws Exception {
        try(BufferedReader br=new BufferedReader(new FileReader("MyGridWorld.txt"))) {
            br.readLine();
    
            Hashtable<String, ArrayList<String[]>> hashTableForWorld=new Hashtable<>();
    
            //read file line by line
            String line = null;
            while ((line = br.readLine()) != null && !line.equals(";;")) {
                //System.out.println("line ="+line);
                String source;
                String action;
    
                //split by tab
                String[] split = line.split("\\t"); 
                source = split[0];
                action = split[1];
                String key = source+","+action;
    
                String[] probDesRew = new String[3];
                probDesRew[0] = split[2];
                probDesRew[1] = split[3];
                probDesRew[2] = split[4];
    
                ArrayList<String[]> value = hashTableForWorld.get(key);
                if(value == null){
                    value = new ArrayList<>();
                    hashTableForWorld.put(key, value);
                }
                value.add(probDesRew);
            }
            return hashTableForWorld;
        }
    }
    

    除了将变量重新定位到其实际使用的位置之外,还将在本地创建返回值,并将读取器包装到一个try-with-resource构造中,该构造确保即使发生异常也会关闭它(请参见官方教程 here ).