计算 java 中元素的频率
Count frequency of elements in java
我正在尝试计算文本文件中所有日期的频率。日期存储在 parsed.get(0) 中但是当我打印频率时我得到这个输出:
1946-01-12: 1
1946-01-12: 1
1946-01-12: 1
1946-01-13: 1
1946-01-13: 1
1946-01-13: 1
1946-01-14: 1
1946-01-14: 1
1946-01-14: 1
1946-01-15: 1
而不是
1946-01-12: 3
1946-01-13: 3
1946-01-14: 3
1946-01-15: 1
我想这是因为我必须存储像 ("1946-01-12", "1946-01-12", "1946-01-12", "1946-01-12", “1946-01-13”、“1946-01-13”、...)。如果我只是打印 parsed.get(0) 我得到
1946-01-12
1946-01-12
1946-01-12
1946-01-13
1946-01-13
1946-01-13
1946-01-14
1946-01-14
1946-01-14
1946-01-15`
根据我下面的代码如何解决?
private static List<WeatherDataHandler> weatherData = new ArrayList<>();
public void loadData(String filePath) throws IOException {
//Read all data
List<String> fileData = Files.readAllLines(Paths.get("filePath"));
System.out.println(fileData);
for(String str : fileData) {
List<String> parsed = parseData(str);
LocalDate dateTime = LocalDate.parse(parsed.get(0));
WeatherDataHandler weather = new WeatherDataHandler(dateTime, Time, temperature, tag);
weatherData.add(weather);
List<String> list = Arrays.asList(parsed.get(0));
Map<String, Long> frequencyMap =
list.stream().collect(Collectors.groupingBy(Function.identity(),
Collectors.counting()));
for (Map.Entry<String, Long> entry : frequencyMap.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
问题
for 循环内的所有内容都在每次迭代时执行。因此,您要重新创建日期集合并重新创建数据流 以便一遍又一遍地进行分析。不好。
解决方案
将流和分析代码移到 for 循环之外。
将您的代码重新考虑为两个阶段。
- 第一阶段是解析输入,将传入数据预处理为您要使用的形式。在这种情况下,我们需要读取一个文本文件,将行解析为
LocalDate
个对象,并将这些对象添加到一个集合中。此代码使用 for 循环。
- 第二阶段是处理重组数据的流工作,
LocalDate
对象的集合。此代码出现在 for 循环 之后。
在我自己的工作中,我确实会将这些要点作为注释放在我的代码中。我会添加分隔线(带有一堆注释或常用符号的注释行)来标记代码中的每个阶段。我可能会将每个阶段作为子例程移动到一个方法中。
顺便说一句,一旦你开始工作,为了好玩,你可能想尝试用流替换读取文件的 for 循环。 Java 可以将文件读取为行流。
我简单测试了这个你可以检查结果
{1946-01-14=3, 1946-01-15=1, 1946-01-12=3, 1946-01-13=3}
原始文件是
1946-01-12: 1
1946-01-12: 1
1946-01-12: 1
1946-01-13: 1
1946-01-13: 1
1946-01-13: 1
1946-01-14: 1
1946-01-14: 1
1946-01-14: 1
1946-01-15: 1
随意修改
代码:
try {
String content = new Scanner(new File("src/main/resources/test.txt")).useDelimiter("\Z").next();
String[] dates= content.split("\n");
Map<String,Long> m
=
Arrays.stream(dates).
map(o->
{return o.split(":")[0];}) //not necessary if you dont have 1s in the text file
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
System.out.println(m.toString());
} catch (FileNotFoundException e) {
e.printStackTrace();
}
用这个你可以得到列表中具有相同值的元素的数量。
int numerOfElements = Collections.frequency(list, "1946-01-12");
根据我认为这是如何工作的,我会按如下方式进行。包含注释以解释其他逻辑。主要思想是尽可能多地在主循环内执行。使用 stream
在循环外创建 frequenceyMap
是额外的和不必要的工作。
private static List<WeatherDataHandler> weatherData =
new ArrayList<>();
public void loadData(String filePath) throws IOException {
// Read all data
List<String> fileData =
Files.readAllLines(Paths.get("filePath"));
System.out.println(fileData);
// Pre-instantiate the freqency map.
Map<String, Long> frequencyMap = new LinkedHashMap<>();
for (String str : fileData) {
List<String> parsed = parseData(str);
LocalDate dateTime =
LocalDate.parse(parsed.get(0));
WeatherDataHandler weather = new WeatherDataHandler(
dateTime, Time, temperature, tag);
weatherData.add(weather);
// Ensure dateTime is a string. This may not have the desired
// format for date but that can be corrected by you
String strDate = dateTime.toString();
// Use the compute method of Map. If the count is null,
// initialize it to 1, otherwise, add 1 to the existing value.
frequencyMap.compute(strDate,
(date, count) -> count == null ? 1 : count + 1);
}
for (Map.Entry<String, Long> entry : frequencyMap
.entrySet()) {
System.out.println(
entry.getKey() + ": " + entry.getValue());
}
}
您还可以打印地图如下:
frequencyMap.forEach((k,v)->System.out.println(k + ": " + v));
最后,上面的内容可以简化一些地方,比如使用 Files.lines(path)
创建流。但是由于您也将其写入 WeatherDataHandler
列表并希望保留您的结构,因此我没有使用该功能。
我正在尝试计算文本文件中所有日期的频率。日期存储在 parsed.get(0) 中但是当我打印频率时我得到这个输出:
1946-01-12: 1
1946-01-12: 1
1946-01-12: 1
1946-01-13: 1
1946-01-13: 1
1946-01-13: 1
1946-01-14: 1
1946-01-14: 1
1946-01-14: 1
1946-01-15: 1
而不是
1946-01-12: 3
1946-01-13: 3
1946-01-14: 3
1946-01-15: 1
我想这是因为我必须存储像 ("1946-01-12", "1946-01-12", "1946-01-12", "1946-01-12", “1946-01-13”、“1946-01-13”、...)。如果我只是打印 parsed.get(0) 我得到
1946-01-12
1946-01-12
1946-01-12
1946-01-13
1946-01-13
1946-01-13
1946-01-14
1946-01-14
1946-01-14
1946-01-15`
根据我下面的代码如何解决?
private static List<WeatherDataHandler> weatherData = new ArrayList<>();
public void loadData(String filePath) throws IOException {
//Read all data
List<String> fileData = Files.readAllLines(Paths.get("filePath"));
System.out.println(fileData);
for(String str : fileData) {
List<String> parsed = parseData(str);
LocalDate dateTime = LocalDate.parse(parsed.get(0));
WeatherDataHandler weather = new WeatherDataHandler(dateTime, Time, temperature, tag);
weatherData.add(weather);
List<String> list = Arrays.asList(parsed.get(0));
Map<String, Long> frequencyMap =
list.stream().collect(Collectors.groupingBy(Function.identity(),
Collectors.counting()));
for (Map.Entry<String, Long> entry : frequencyMap.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
问题
for 循环内的所有内容都在每次迭代时执行。因此,您要重新创建日期集合并重新创建数据流 以便一遍又一遍地进行分析。不好。
解决方案
将流和分析代码移到 for 循环之外。
将您的代码重新考虑为两个阶段。
- 第一阶段是解析输入,将传入数据预处理为您要使用的形式。在这种情况下,我们需要读取一个文本文件,将行解析为
LocalDate
个对象,并将这些对象添加到一个集合中。此代码使用 for 循环。 - 第二阶段是处理重组数据的流工作,
LocalDate
对象的集合。此代码出现在 for 循环 之后。
在我自己的工作中,我确实会将这些要点作为注释放在我的代码中。我会添加分隔线(带有一堆注释或常用符号的注释行)来标记代码中的每个阶段。我可能会将每个阶段作为子例程移动到一个方法中。
顺便说一句,一旦你开始工作,为了好玩,你可能想尝试用流替换读取文件的 for 循环。 Java 可以将文件读取为行流。
我简单测试了这个你可以检查结果
{1946-01-14=3, 1946-01-15=1, 1946-01-12=3, 1946-01-13=3}
原始文件是
1946-01-12: 1
1946-01-12: 1
1946-01-12: 1
1946-01-13: 1
1946-01-13: 1
1946-01-13: 1
1946-01-14: 1
1946-01-14: 1
1946-01-14: 1
1946-01-15: 1
随意修改
代码:
try {
String content = new Scanner(new File("src/main/resources/test.txt")).useDelimiter("\Z").next();
String[] dates= content.split("\n");
Map<String,Long> m
=
Arrays.stream(dates).
map(o->
{return o.split(":")[0];}) //not necessary if you dont have 1s in the text file
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
System.out.println(m.toString());
} catch (FileNotFoundException e) {
e.printStackTrace();
}
用这个你可以得到列表中具有相同值的元素的数量。
int numerOfElements = Collections.frequency(list, "1946-01-12");
根据我认为这是如何工作的,我会按如下方式进行。包含注释以解释其他逻辑。主要思想是尽可能多地在主循环内执行。使用 stream
在循环外创建 frequenceyMap
是额外的和不必要的工作。
private static List<WeatherDataHandler> weatherData =
new ArrayList<>();
public void loadData(String filePath) throws IOException {
// Read all data
List<String> fileData =
Files.readAllLines(Paths.get("filePath"));
System.out.println(fileData);
// Pre-instantiate the freqency map.
Map<String, Long> frequencyMap = new LinkedHashMap<>();
for (String str : fileData) {
List<String> parsed = parseData(str);
LocalDate dateTime =
LocalDate.parse(parsed.get(0));
WeatherDataHandler weather = new WeatherDataHandler(
dateTime, Time, temperature, tag);
weatherData.add(weather);
// Ensure dateTime is a string. This may not have the desired
// format for date but that can be corrected by you
String strDate = dateTime.toString();
// Use the compute method of Map. If the count is null,
// initialize it to 1, otherwise, add 1 to the existing value.
frequencyMap.compute(strDate,
(date, count) -> count == null ? 1 : count + 1);
}
for (Map.Entry<String, Long> entry : frequencyMap
.entrySet()) {
System.out.println(
entry.getKey() + ": " + entry.getValue());
}
}
您还可以打印地图如下:
frequencyMap.forEach((k,v)->System.out.println(k + ": " + v));
最后,上面的内容可以简化一些地方,比如使用 Files.lines(path)
创建流。但是由于您也将其写入 WeatherDataHandler
列表并希望保留您的结构,因此我没有使用该功能。