如何从数组列表中提取项目并对其进行计数,以便从元素及其总计数(正确)在 Java 中创建映射?
Posted
技术标签:
【中文标题】如何从数组列表中提取项目并对其进行计数,以便从元素及其总计数(正确)在 Java 中创建映射?【英文标题】:How do you pull items from an array list and count them in order to create a map from the element and its total count (properly) in Java? 【发布时间】:2014-11-12 15:07:10 【问题描述】:我需要知道如何正确地遍历元素的 ArrayList 并在事先不知道元素的情况下计算列表中出现的元素的数量。我尝试了几种方法,目前有一种方法可行,但我觉得它丑陋且不合适。
为了更深入地解释一下,我有一个 java web 应用程序,其中一个页面将显示一个图表,显示网站在一天/一周/一个月内获得的点击次数。我正在获取网站被点击的日期并将它们放入数组列表中,遍历数组列表,获取一个元素并计算它出现的次数,然后将日期作为字符串及其总数并将其放入作为 (String,int) 的键值对转换为映射,然后我将获取此映射并将其转换为 JSON 对象,并将其与 D3JS 库一起使用来创建一个漂亮的条形图来描述信息。以下是我尝试过的代码及其呈现的结果。结果是准确的,但不仅仅是工作,我想正确而优雅地完成它,我知道我在这里没有做到这一点。我会尽可能彻底地解释它,并期待更好的方法的反馈和想法。
测试代码主方法:
public static void main(String[] args)
Map<String, Integer> compMap = new HashMap<String, Integer>();
ArrayList<String> comp = new ArrayList<String>();
comp.add("10-31-2014");
comp.add("10-31-2014");
comp.add("10-31-2014");
comp.add("10-31-2014");
comp.add("10-15-2014");
comp.add("10-15-2014");
comp.add("10-15-2014");
comp.add("10-15-2014");
comp.add("10-15-2014");
comp.add("10-12-2014");
comp.add("10-12-2014");
comp.add("10-12-2014");
comp.add("10-12-2014");
comp.add("10-12-2014");
comp.add("10-12-2014");
comp.add("10-12-2014");
comp.add("10-12-2014");
comp.add("10-12-2014");
comp.add("10-09-2014");
comp.add("10-09-2014");
comp.add("10-09-2014");
comp.add("10-09-2014");
comp.add("10-09-2014");
comp.add("10-01-2014");
comp.add("10-01-2014");
comp.add("10-01-2014");
comp.add("10-01-2014");
comp.add("10-01-2014");
comp.add("10-01-2014");
comp.add("10-01-2014");
// 01 = 7 : 09 = 5 : 12 = 9 : 15 = 5 : 31 = 4
for (int i = 0; i < comp.size(); i++)
int counter = 0;
String current = comp.get(i);
for (int j = 0; j < comp.size(); j++)
if (comp.get(j).equals(comp.get(i)))
counter++;
compMap.put(current, counter);
for (Map.Entry<String,Integer> entry : compMap.entrySet())
System.out.println("Key Value Pair Is: " + entry.getKey() + " : " + entry.getValue());
出于测试目的,我创建了一个数组列表并向其中添加了多个字符串日期(老实说,我本可以使用数组并减少测试代码,但我希望尽可能接近实际情况)。之后我创建了一个嵌套 for 循环,外部 for 循环抓取列表中的第一项并将其用作地图中键的当前日期,它还负责从 0 开始计数器。内部 for 循环开始于零,无论我在哪里,并抓取列表中的元素以将它们与元素 i 进行比较,如果它们相等,则计数器增加 1。一旦在退出外循环之前完成循环,当前正在使用的日期和计数器总计被添加到地图中。
计数器的结果:
Key Value Pair Is: 10-15-2014 : 5
Key Value Pair Is: 10-12-2014 : 9
Key Value Pair Is: 10-31-2014 : 4
Key Value Pair Is: 10-01-2014 : 7
Key Value Pair Is: 10-09-2014 : 5
这些是在测试运行后从控制台中提取的。结果是正确的。我的问题更多是看看是否有更优雅或更合适的方法来执行这项任务?
谢谢!
【问题讨论】:
一些正交提示... 要将日期时间值序列化为字符串,请遵循标准 ISO 8601 格式。在您的 Java 代码中,使用在 Joda-Time 库或 Java 8 的 java.time 包(受 Joda-Time 启发)中找到的日期时间类。两者都提供了可以代表此数据的LocalDate
。在解析/生成字符串表示时,两者都使用 ISO 8601 作为默认值。
【参考方案1】:
Multiset
我建议Google Guava 中的MultiSet
集合。它具有很好的功能,可以为您节省大量时间。
com.google.common.collect.Multiset<String> multiset = com.google.common.collect.HashMultiset.create();
multiset.add("10-31-2014");
multiset.add("10-31-2014");
multiset.add("10-15-2014");
multiset.add("10-15-2014");
multiset.add("10-15-2014");
multiset.add("10-15-2014");
multiset.toString();
打印:
[10-31-2014 x 2, 10-15-2014 x 4]
【讨论】:
【参考方案2】:地图
for(String arrayElement : comp)
if(!compMap.containsKey(arrayElement))
compMap.put(arrayElement, 1);
else
int newCount = compMap.get(arrayElement) + 1;
compMap.put(arrayElement, newCount);
地图的全部好处是您可以非常快速地对其进行查找。这允许您在扫描阵列一次时进行“计数”。这使得算法的运行时间显着加快(理论上,我们从 O(n^2) 提高到 O(n))。
我们也可以使用 java 的 for-each
循环(就像我在这里所做的那样),而不需要在 for
循环中维护一个单独的计数器(即 i
变量)。这只是为了美观 - 它使代码更易于阅读,但不会从根本上改变算法。
【讨论】:
这正是我要找的,我知道我的代码在列表中重复了很多次,而且它体积庞大且难以阅读,我不会抹黑这里的其他回复但这是我接受的答案。非常感谢。 @vaxquis - 虽然我不会撤消您的编辑,但我更喜欢==false
而不是 !
语法。查看代码时(恕我直言)很容易误读!
。这是相当主观的,我知道。
谢谢。我这样做是为了提供最常见的编码风格——从库代码到教程,内联大括号在 Java(甚至有时在 C/C++)中无处不在;至于后者...== false
位于行尾,而!
位于最开始 - 这使得!
更难跳过,尤其是。用长线。不过,那只是一种意见。我进行了编辑,因为对此的共识非常支持 !bool 语法,***.com/questions/2661110/… 等。【参考方案3】:
您的代码进行了大量重复计数。我会使用 Set 来避免这种情况。还有Collections.frequency()
方法来统计出现次数:
Set<String> compSet = new HashSet<String>(comp); // unique elements of comp
for (String s : compSet)
compMap.put(s, Collections.frequency(comp, s));
【讨论】:
这仍然是一个 O(n^2) 算法。这是完全没有必要的,因为存在一个相当明显的 O(n) 解决方案。 我也喜欢这个答案,但是@user2321368 在这种情况下是正确的,不需要多次遍历列表,这最终是我在优雅和时间方面所寻找的,我正在检查列表中的许多项目,我希望它尽可能以最佳状态运行。非常感谢您的回复并花时间帮助别人。 @James 不是解决这个特定问题的最佳方法,但感谢您的发帖。我不知道Collections
中有那个方便的实用程序。对于不关心性能的一小部分数据,我宁愿使用这个更短的代码。以上是关于如何从数组列表中提取项目并对其进行计数,以便从元素及其总计数(正确)在 Java 中创建映射?的主要内容,如果未能解决你的问题,请参考以下文章