我遇到以下设计问题,希望得到您的帮助解决。
下面是代码的简化视图
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));
}
}
如您所见,类“ DataProcessor”的“ processData”方法采用DataFile对象,处理整个文件,为每一行创建Record对象,然后返回“ Record”对象的列表。
我对“ processData”方法的问题:当原始文件确实很大时,“记录列表”对象会占用大量内存,有时程序会失败。我需要更改当前设计,以便最大程度地减少内存使用。 “ DataProcessor”不应直接访问“ DatabaseManager”。
我当时正在考虑将队列传递给“ processData”方法,其中一个线程运行“ processData”方法以在队列中插入“ Record”对象,而另一个线程从队列中删除“ Record”对象并将其插入数据库。但是我不确定与此相关的性能问题。
最佳答案
将驱动过程的责任放到最受约束的资源(在您的情况下为DataProcessor
)中-这将确保最好地遵守约束,而不是强迫达到极限。
注意:甚至不用考虑多线程,它对处理文件没有任何帮助。如果您的数据通过网络传输,线程将是一个解决方案,当您不知道何时下一个数据块将要到达广告时,与等待CPU直到等待母牛回家休息相比,也许与您的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);
}
}
[edit]解决“但我们确定了什么以及如何确定,现在您要告诉我们我们需要编写额外的代码?”
建立一个
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输入文件而言甚至是不可能的)的操作变得“尽可能快”。