我有一个包含 500,000 行数据和 22 列数据的 CSV 文件。该数据代表美国一年内的所有商业航类。我的任务是查找数据集中飞行里程最多的飞机的尾号。第 5 列包含航空公司每次航类的尾号。第 22 列包含行驶的总距离。
请参阅下面我的 extractQ3
方法。首先,使用 createHashMap()
方法为整个 CSV 创建一个 HashMap
。然后,我运行了一个 for
循环来识别数据集中的每个唯一尾号,并将它们存储在一个名为 tailNumbers
的数组中。然后,对于每个唯一的尾号,我循环遍历整个 Hashmap
以计算该尾号的总距离英里。
代码在较小的数据集上运行良好,但一旦大小增加到 500,000 行,代码就会变得非常低效,并且需要很长时间才能运行。谁能给我提供一种更快的方法来做到这一点?
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;
}
}
最佳答案
答案取决于“最有效”、“极其低效”和“需要永恒”的含义。这些是主观术语。答案可能还取决于特定的技术因素(速度与内存消耗;唯一飞行 key 的数量与总体记录数量的比较;等等)。
我建议首先对您的代码进行一些基本的简化。看看这是否会给你带来更好(可接受)的结果。如果您需要更多,那么您可以考虑更高级的改进。
无论您做什么,请花一些时间来了解您所做的任何更改的广泛影响。
专注于从“可怕”到“可接受” - 然后担心之后进行更高级的调整(如果您仍然需要它)。
考虑使用 BufferedReader
而不是 Scanner
。请参阅here 。尽管扫描仪可能足以满足您的需求(即,如果它不是瓶颈)。
考虑使用扫描仪循环中的逻辑来捕获一次数据传递中的尾号和累积里程。为了清晰和简单起见,以下内容特意是基本的:
// 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:循环遍历 CSV 并为另一列中的每个唯一值求和一列值的最有效方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60688729/