2016-10-20 33 views
0

我有以下設計問題,希望得到您的幫助來解決。 下面的代碼是什麼樣子我可以使用什麼數據結構或設計模式來解決此問題

class DataProcessor{ 
    public List<Record> processData(DataFile file){ 
     List<Record> recordsList = new ArrayList<Record>(); 
     for(Line line : file.getLines()){ 
      String processedData = processData(line); 
      recordsList.add(new Record(processedData)); 
     } 
    } 

    private String processData(String rawLine){ 
     //code to process line 
    } 
} 
class DatabaseManager{ 
    saveRecords(List<Record> recordsList){ 
     //code to insert records objects in database 
    } 
} 
class Manager{ 
    public static void main(String[] args){ 

     DatabaseManager dbManager = new DatabaseManager("e:\\databasefile.db"); 
     DataFile dataFile = new DataFile("e:\\hugeRawFile.csv"); 
     DataProcessor dataProcessor = new DataProcessor(); 
     dbManager.saveRecords(dataProcessor.processData(dataFile)); 
    } 
} 

一個簡單的外觀正如你所看到的,階級的「過程數據」方法「數據處理器」採用數據文件的對象,處理整個文件,創建記錄對象的每一行,然後它返回一個「記錄」對象列表。

我與「processData」方法的問題:當原始文件非常龐大時,「記錄列表」對象佔用大量內存,有時程序失敗。我需要更改當前的設計,以便將內存使用量降至最低。 「DataProcessor」不應直接訪問「DatabaseManager」。 我正在考慮將隊列傳遞給「processData」方法,其中一個線程運行「processData」方法在隊列中插入「Record」對象,而另一個線程從隊列中移除「Record」對象並將其插入到數據庫中。但我不確定這個問題的性能問題。

回答

0

您應該可以使用流式處理在一個線程中執行此操作,一次在內存中執行一條記錄。實現取決於你的DatabaseManager使用的技術。

+0

請您詳細說明一下嗎? DatabaseManager使用SQLite的JDBC接口,以及簡單的準備好的語句,其中我從「Record」對象中獲取值並將它們放入「insert」語句中,然後執行此插入語句。 –

+1

這裏是一個例子:https://tododev.wordpress.com/2014/08/14/creating-and-object-stream-from-a-jdbc-resultset/ –

1

將推動過程的責任推入最受限制的資源(在您的案例中爲DataProcessor) - 這將確保約束條件得到最好的遵守,而不是被迫進入突破點。

注意:甚至不認爲多線程的,它不會給你帶來任何好的處理文件。如果你的數據通過網絡傳遞,當你不知道你的下一個數據塊何時到達時,線程將是一個解決方案,或許你有更好的方法來處理你的CPU時間,而不是等待「直到母牛回家休息「(笑)。但用文件?你知道這項工作有一個開始和結束,所以儘快開始工作。

class DataProcessor{ 
    public List<Record> processData(DataFile file){ 
     List<Record> recordsList = new ArrayList<Record>(); 
     for(Line line : file.getLines()){ 
      String processedData = processData(line); 
      recordsList.add(new Record(processedData)); 
     } 
    } 

    private String processData(String rawLine){ 
     //code to process line 
    } 

    public void processAndSaveData(DataFile dataFile, DatabaseManager db) { 
     int maxBuffSize=1024; 
     ArrayList<Record> buff=new ArrayList<Record>(maxBuffSize); 
     for(Line line : file.getLines()){ 
     String processedData = processData(line); 
     buff.add(new Record(processedData)); 
     if(buff.size()==maxBuffSize) { 
      db.saveRecords(buff); 
      buff.clear(); 
     } 
     } 
     // some may be still unsaved here, less that maxBuffSize 
     if(buff.size()>0) { 
     db.saveRecords(buff); 
     // help the CG, let it recycle the records 
     // without needing to look "is buff still reacheable"? 
     buff.clear(); 
     } 
    } 
} 

class Manager{ 
    public static void main(String[] args){ 

     DatabaseManager dbManager = new DatabaseManager("e:\\databasefile.db"); 
     DataFile dataFile = new DataFile("e:\\hugeRawFile.csv"); 
     DataProcessor dataProcessor = new DataProcessor(); 

     // So... do we need another stupid manager to tell us what to do? 
     // dbManager.saveRecords(dataProcessor.processData(dataFile)); 

     // Hell, no, the most constrained resource knows better 
     // how to deal with the job! 
     dataProcessor.processAndSaveData(dataFile, dbManager); 
    } 
} 

[編輯]尋址「但我們決定你是來告訴我們,我們需要編寫額外的代碼什麼和如何,現在?」

建立一個AbstractProcessor類,並要求你的伴侶只是從它派生。

class AbstractProcessor { 
    // sorry, need to be protected to be able to call it 
    abstract protected Record processData(String rawLine); 

    abstract protected Class<? extends Record> getRecordClass(); 

    public void processAndSaveData(DataFile dataFile, DatabaseManager db) { 
    Class<? extends Record> recordType=this.getRecordClass(); 
    if(recordType.equals(MyRecord1.class) { 
     // buffered read and save MyRecord1 types specifically 
    } 
    else if(recordType.equals(YourRecord.class)) { 
     // buffered read and save YourRecord types specifically 
    } 
    // etc... 
    } 
} 

現在,所有他們需要做的是「代碼」 extends AbstractProcessor,使他們processData(String)保護,寫一個簡單的方法聲明的記錄類型(也可以被一個枚舉)。這不像你要求他們付出巨大的努力,並且使得「儘可能快」的操作成本高(甚至不可能,對於TB輸入文件)操作。

+0

謝謝你。實際上我曾經有過類似的東西。但在我的真實程序中,我有很多類爲不同類型的數據擴展「DataProcessor」。這些類由不同的開發人員開發,需求是「數據處理器」類只需要一個數據文件,對「DatabaseManager」類一無所知。所以我重構了與我提到的代碼類似的代碼,但後來我遇到了內存問題。順便說一句,我仍然是初學者程序員。 –

+1

@AnthonyJ。好的,在我的答案結尾處看到我的額外評論。如果你的隊友不同意,你註定要完成這項任務 - 沒有辦法避開它。 –

+0

好吧,再次感謝,是的,這不需要太多的工作在他們身邊。但我想知道,你爲什麼認爲隊列不起作用?如果「管理器」將隊列傳遞給「數據處理器」,則「管理器」從該隊列中取出「記錄」並將它們傳遞給「數據庫」管理器。 –

相關問題