Java:最有效的方法是循环CSV,并为另一列中的每个唯一值求和一列的值



我有一个CSV文件,其中包含500000行数据和22列。该数据代表了美国一年内的所有商业航班。我的任务是找出数据集中飞行里程最多的飞机的尾部编号。第5栏包含了每架飞机的机尾号码。第22列包含行驶的总距离。

请参阅下面我的extractQ3方法。首先,使用createHashMap()方法为整个CSV创建一个HashMap。然后,我运行一个for循环来识别数据集中的每个唯一尾号,并将它们存储在一个名为tailNumbers的数组中。然后,对于每个唯一的尾号,我遍历整个Hashmap,以计算该尾号的总距离。

代码在较小的数据集上运行良好,但一旦大小增加到500000行,代码就会变得非常低效,并且需要很长时间才能运行。有人能给我一个更快的方法吗?

public class FlightData {
HashMap<String,String[]>  dataMap;
public static void main(String[] args) {
FlightData map1 = new FlightData();
map1.dataMap = map1.createHashMap();
String answer = map1.extractQ3(map1);  
}
public String extractQ3(FlightData map1) {
ArrayList<String> tailNumbers = new ArrayList<String>();
ArrayList<Integer> tailMiles = new ArrayList<Integer>();
//Filling the Array with all tail numbers
for (String[] value : map1.dataMap.values()) {
if(Arrays.asList(tailNumbers).contains(value[4])) {  
} else {
tailNumbers.add(value[4]);
}
}
for (int i = 0; i < tailNumbers.size(); i++) {
String tempName = tailNumbers.get(i); 
int miles = 0;
for (String[] value : map1.dataMap.values()) {
if(value[4].contentEquals(tempName) && value[19].contentEquals("0")) {
miles = miles + Integer.parseInt(value[21]);
}  
}
tailMiles.add(miles);     
}
Integer maxVal = Collections.max(tailMiles);
Integer maxIdx = tailMiles.indexOf(maxVal);
String maxPlane = tailNumbers.get(maxIdx);
return maxPlane;
}


public HashMap<String,String[]> createHashMap() {
File flightFile = new File("flights_small.csv");
HashMap<String,String[]> flightsMap = new HashMap<String,String[]>();
try {
Scanner s = new Scanner(flightFile);
while (s.hasNextLine()) {
String info = s.nextLine();
String [] piecesOfInfo = info.split(",");
String flightKey = piecesOfInfo[4] + "_" + piecesOfInfo[2] + "_" + piecesOfInfo[11]; //Setting the Key
String[] values = Arrays.copyOfRange(piecesOfInfo, 0, piecesOfInfo.length);
flightsMap.put(flightKey, values);
}

s.close();
}

catch (FileNotFoundException e)
{
System.out.println("Cannot open: " + flightFile);
}
return flightsMap;
}
}

答案取决于你所说的"最高效"、"效率极低"one_answers"需要很长时间"。这些都是主观的术语。答案也可能取决于特定的技术因素(速度与内存消耗;与整体记录数量相比的唯一飞行密钥数量;等等(

首先,我建议对代码进行一些基本的精简。看看这是否能给你带来更好(可接受(的结果。如果你需要更多,那么你可以考虑更高级的改进。

无论你做什么,都要花一些时间来了解你所做的任何改变的广泛影响

专注于从"糟糕"到"可接受",然后担心之后会有更高级的调整(如果你仍然需要的话(。

考虑使用BufferedReader而不是Scanner。请参见此处。尽管扫描仪可能正好满足您的需求(即,如果它不是瓶颈(。

考虑在扫描仪循环中使用逻辑来捕获一次数据中的尾号和累计里程数。为了清晰和简单,以下内容是刻意的基础:

// The string is a tail number.
// The integer holds the accumulated miles flown for that tail number:
Map<String, Integer> planeMileages = new HashMap();
if (planeMileages.containsKey(tailNumber)) {
// add miles to existing total:
int accumulatedMileage = planeMileages.get(tailNumber) + flightMileage;
planeMileages.put(tailNumber, accumulatedMileage);
} else {
// capture new tail number:
planeMileages.put(tailNumber, flightMileage);
}

之后,一旦你完成了扫描循环,你就可以迭代你的planeMileages来找到最大的里程数:

String maxMilesTailNumber;
int maxMiles = 0;
for (Map.Entry<String, Integer> entry : planeMileages.entrySet()) {
int planeMiles = entry.getValue();
if (planeMiles > maxMiles) {
maxMilesTailNumber = entry.getKey();
maxMiles = planeMiles;
}
}

警告-此方法仅用于说明。它将只捕获一个尾号。可能有多架飞机的最大里程数相同。你必须调整你的逻辑来捕捉多个"赢家"。

上述方法消除了对几个现有数据结构和相关处理的需要。

如果您仍然面临问题,请设置一些计时器来查看代码的哪些特定区域最慢,然后您将有更多可以关注的特定调优机会。

我建议您使用java 8 Stream API,这样您就可以利用并行流。

最新更新