簡體   English   中英

Java-從文本文件中的平面結構讀取層次結構數據並構建哈希圖

[英]Java - Read Hierarchical data from flat structure in text file and build hashmap

我有一個文本文件,該文本文件中的分層結構中的分層數據可用。

child parent
Y,     X
Z,     Y
A,     Z

就像X是Y的父級,Y本身是Z的父級,Z又是A的父級。它可以按任何順序出現在文件中。 我需要構建一個哈希圖,其中鍵應該是元素,值應該是所有祖先元素的列表。 例如,哈希圖應具有基於上述數據的條目,例如

A = [Z,Y,X],Y = [X],Z = [Y,X]。

我已經在Java中編寫了代碼來構建此哈希圖。 只需要知道是否有更有效的方法可以做到這一點。 邏輯是

  1. 讀取哈希圖中的整個文件,其中孩子是鍵,父是值
  2. 從上面創建的哈希圖中,遞歸遍歷每個孩子並建立父母列表。

     public class Test { public static final String FILE_NAME = "dataset1"; public static final HashMap<String,String> inputMap = new HashMap<String,String>(); public static final Map<String, ArrayList<String>> parentChildMap = new HashMap<String,ArrayList<String>>(); private static void readTextFile(String aFileName) throws IOException { Path path = Paths.get(aFileName); try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)){ String line = null; while ((line = reader.readLine()) != null) { String[] dataArray = line.split(","); String child = dataArray[0]; String parent = dataArray[1]; inputMap.put(child, parent); } } } public static ArrayList<String> getParents(String childId, ArrayList<String> parents) { if (childId == null) return parents; String parentId = inputMap.get(childId); if(parentId!=null) parents.add(parentId); getParents(parentId, parents); return parents; } public static void main(String[] s) throws IOException { readTextFile(FILE_NAME); for(String child : inputMap.keySet()) { ArrayList<String> parents = getParents(child, new ArrayList<String>()); parentChildMap.put(child, parents); } } 

遞歸已經非常有效。 您可以優化以下內容:

  • 將遞歸放入循環
  • 在遞歸/循環中使用備忘錄(避免重新計算)
  • 不要在每次調用getParent時重新計算祖先,不要預先計算結果並存儲它們

這是我的代碼:

import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class Test {
    public static final String FILE_NAME = "dataset1";
    public static final HashMap<String, String> inputMap = new HashMap<String, String>();
    public static final Map<String, ArrayList<String>> parentChildMap = new HashMap<String, ArrayList<String>>();

    private static void readTextFile(String aFileName) throws IOException {

        Path path = Paths.get(aFileName);

        try (BufferedReader reader = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
            String line = null;
            while ((line = reader.readLine()) != null) {
                String[] dataArray = line.split(",");
                String child = dataArray[0];
                String parent = dataArray[1];

                inputMap.put(child, parent);
            }
        }

        // this replaces the recursion:
        for (String k : inputMap.keySet()) {
            String ok = k;
            ArrayList<String> tmp = new ArrayList<String>();
            while (true) {
                // if this has already been computed, use old answer
                if (parentChildMap.containsKey(k)) {
                    tmp.addAll(parentChildMap.get(k));
                    break;
                }
                if (inputMap.containsKey(k)) {
                    String v = inputMap.get(k);
                    tmp.add(v);
                    k = v;
                } else {
                    break;
                }
            }
            parentChildMap.put(ok, tmp);
        }
    }

    public static ArrayList<String> getParents(String childId) {
        // do not recompute
        return parentChildMap.get(childId);
    }
}

您要的是“更有效的方法”,所以這是我的批評(次要)和建議。

  • 不要將line初始化為null 只是聲明它。
  • 不要使用split() 它可能會拆分為兩個以上的值,並且必須創建一個數組。 只需使用indexOf()

因此,第一種方法變為(壓縮一些):

public static final Map<String, String> inputMap = new HashMap<>();
private static void readTextFile(String aFileName) throws IOException {
    try (BufferedReader reader = Files.newBufferedReader(Paths.get(aFileName),
                                                         StandardCharsets.UTF_8)){
        for (String line; (line = reader.readLine()) != null; ) {
            int idx = line.indexOf(',');
            inputMap.put(/*child*/line.substring(0, idx),
                         /*parent*/line.substring(idx + 1));
        }      
    }
}

現在提出建議。

您的代碼多次解析相同的父級,例如,檢索A父級時,它必須遍歷整個父鏈ZYX ,而檢索Z的父級時,則必須遍歷父鏈YX 您多次進行相同的步行。

只執行一次會更有效。 由於數據是無序的,因此必須使用遞歸進行處理。 我已經將parentChildMap重命名為一個更合適的ancestorMap

public static final Map<String, List<String>> ancestorMap = new HashMap<>();
private static List<String> getAncestors(String child) {
    // Check if ancestors already resolved
    List<String> ancestors = ancestorMap.get(child);
    if (ancestors == null) {
        // Find parent
        String parent = inputMap.get(child);
        if (parent == null) {
            // Child has no parent, i.e. no ancestors
            ancestors = Collections.emptyList();
        } else {
            // Find ancestors of parent using recursive call
            List<String> parentAncestors = getAncestors(parent);
            if (parentAncestors.isEmpty()) {
                // Parent has no ancestors, i.e. child has single ancestor (the parent)
                ancestors = Collections.singletonList(parent);
            } else {
                // Child's ancestors is parent + parentAncestors
                ancestors = new ArrayList<>(parentAncestors.size() + 1);
                ancestors.add(parent);
                ancestors.addAll(parentAncestors);
            }
        }
        // Save resolved ancestors
        ancestorMap.put(child, ancestors);
    }
    return ancestors;
}

如果您不關心使用emptyList()singletonList()的優化或有注釋,可以將其壓縮為:

private static List<String> getAncestors(String child) {
    List<String> ancestors = ancestorMap.get(child);
    if (ancestors == null) {
        ancestorMap.put(child, ancestors = new ArrayList<>());
        String parent = inputMap.get(child);
        if (parent != null) {
            ancestors.add(parent);
            ancestors.addAll(getAncestors(parent));
        }
    }
    return ancestors;
}

然后, main方法變為:

public static final String FILE_NAME = "dataset1";
public static void main(String[] args) throws IOException {
    readTextFile(FILE_NAME);
    for (String child : inputMap.keySet())
        getAncestors(child); // Ignore return value
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM