【问题标题】:How to stop repeated print in java如何在java中停止重复打印
【发布时间】:2014-05-02 16:12:40
【问题描述】:

我正在使用以下代码从 ArrayList 打印项目:

for(int i2 = 0; i2 < a.size(); i2++)
    {
        word2 = a.get(i2);
        for(int j2 = 0; j2 < a.size(); j2++)
        {
            if(word2.equals(a.get(j2)))
            {
                counter++;
            }
        }
        if(counter!=0)
        {
            System.out.println(word2 + " : " + counter);
        }
        counter = 0;
    } 

当我打印时,我不想打印出副本。就像现在一样,它将打印

Alphabet : 3
Alright : 3
Apple : 3
Alphabet : 3
Alright : 3
Apple : 3
Alphabet : 3
Alright : 3
Apple : 3

我只想打印

Alphabet : 3
Alright : 3
Apple : 3

如何使它不打印重复项?我必须使用 ArrayList 进行分配

【问题讨论】:

  • 使用Set 而不是List
  • @JarrodRoberson A set 将从列表中删除重复项,从而丢失有关字数的信息。
  • 你可以从这里获得一些帮助:stackoverflow.com/questions/22412035/…
  • 合适的集合是Multimap
  • @JarrodRoberson A Multiset 会更合适(Multimap 需要一些任意值对象),但想法相同。单词可以插入到例如 Guava 的 Multiset 中,然后 count() 将返回给定单词的单词计数。您应该发布一个示例作为答案,这将是另一种不错的干净方法。

标签: java for-loop


【解决方案1】:

使用TreeMap&lt;String, Integer&gt; 跟踪字数

SortedMap<String, Integer> wordFrequencyMap = new TreeMap<String, Integer>();

for (String str : a) {
  if (wordFrequencyMap.containsKey(str)) {
    int strFreq = Integer.intValue(wordFrequencyMap.get(str));
    strFreq++;
    wordFrequencyMap.put(str, new Integer(strFreq));
  }
  else {
    wordFrequencyMap.put(str, new Integer(1));
  }
}

for (String word : wordFrequencyMap.keySet()) {
  System.out.println(word + " : " + wordFrequencyMap.get(word));
}

这种数据结构不允许重复,它会计算每个单词的出现次数,只需要遍历列表一次。由于您使用的是 TreeMapString 键,因此它会在迭代时按字母顺序打印键

【讨论】:

  • +1 补充一点:在更多性能受限的环境中,一个常见的优化是定义一个自定义的可变计数器类,该类包装一个原始int,以便可以在不创建计数的情况下更新计数new Integer。在 Apache Commons 中也有 MutableInt
【解决方案2】:

另一种选择,虽然性能不是最好的(尽管它对您的应用程序来说已经足够了,并且与您当前的代码具有相似的性能特征),但是创建一个临时的Set 来保存唯一词列表,然后使用Collections.frequency() 计算原始列表中的出现次数,例如和你的ArrayList&lt;String&gt; a:

Set<String> unique = new HashSet<String>(a);

for (String word : unique)
    System.out.println(word + " : " + Collections.frequency(a, word));

甚至只是:

for (String word : new HashSet<String>(a))
    System.out.println(word + " : " + Collections.frequency(a, word));

这里的好处是代码简洁明了。

如果要按字母顺序打印单词,可以使用TreeSet,如果要按第一次出现的顺序打印,可以使用LinkedHashSet

顺便说一句,上面没有存储计数以供以后使用,您的原始代码也没有这样做。但是,如果您想这样做,将结果存储在地图中很简单:

Map<String,Integer> wordCounts = new HashMap<String,Integer>();

for (String word : new HashSet<String>(a))
    wordCounts.put(word, Collections.frequency(a, word));

// wordCounts now contains a map of strings -> counts.    

【讨论】:

  • 不错!我不知道这个Collections.frequency 方法。它是如何工作的?添加对象时,频率是否存储在某处?这种方法的时间复杂度是多少?
  • @BrianVanover 我添加了一个文档链接。 frequency() 没有以任何方式优化,每次调用在容器大小上都是 O(n) ;它只是遍历容器并根据equals() 计算等于指定对象的元素。它类似于 OP 的原始实现。这种技术可能不适合大词集,但对于简单应用中的小词集,性能一般都可以接受。
  • 整个事情是 O(mn),其中 m = 唯一词数,n = 总词数;没有单词重复时最坏情况 O(n^2),所有单词都相同时最好情况 O(n)。
  • 谢谢。我对遍历容器感到困惑。也许我需要对 Sets 的底层流程进行更多说明。不会将重复项添加到 Set 中,因此不会引用重复项进行计数?
  • @BrianVanover 请注意,Collections.frequency(a, word) 计算原始 ArrayList a 中的频率,而不是集合中的频率。
【解决方案3】:

另一个 Java-8 流替代方案:

这会在collect 步骤创建一个映射:键是单词(因为Function.identity() 返回每​​个单词),值是频率(因为Collectors.counting() 返回每​​个单词的频率)。 forEach 步骤只打印每个条目 "&lt;word&gt;: &lt;word-frequency&gt;"

a.stream().collect(Collectors.groupingBy(
               Function.identity(),
               Collectors.counting()))
          .forEach((word, frequency) -> System.out.println(word+": "+frequency));

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-21
    • 1970-01-01
    • 1970-01-01
    • 2022-12-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多