【问题标题】:Frequency of words in List of Strings of Strings字符串列表中单词的频率
【发布时间】:2014-10-16 08:59:07
【问题描述】:

我有一个字符串列表:

List<String> terms = ["Coding is great", "Search Engines are great", "Google is a nice search engine"]

如何获取列表中每个单词的频率: 例如{Coding:1, Search:2, Engines:1, engine:1, ....}

这是我的代码:

    Map<String, Integer> wordFreqMap = new HashMap<>(); 
    for (String contextTerm : term.getContexTerms()  ) 
                {
                    String[] wordsArr = contextTerm.split(" ");
                    for (String  word : wordsArr) 
                    {
                        Integer freq = wordFreqMap.get(word); //this line is getting reset every time I goto a new COntexTerm
                        freq = (freq == null) ? 1: ++freq;
                        wordFreqMap.put(word, freq);
                    }
                }

【问题讨论】:

  • 问之前有没有尝试过?
  • 是的,我的方法是使用 2 个循环。那两个循环在另一个循环下。所以 3 个循环让它变得很慢
  • 通过将其包含在您的问题中并将其移至 codereview,您可能会获得更好的帮助。
  • 或者,如果它是您认为遇到问题的特定技术,请将一些(相关)代码放入问题中,在这里就可以了。
  • "...让它变得很慢" - 所以真正的问题根本不是 如何 去做,而是如何去做(更多)有效地 比您目前的方法? (我在下面提出的解决方案使用并行流,可能在这方面带来优势,但我还没有(还)以任何方式对其进行基准测试......)

标签: java string list arraylist


【解决方案1】:

Java 8 流的惯用解决方案:

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class SplitWordCount
{
    public static void main(String[] args)
    {
        List<String> terms = Arrays.asList(
            "Coding is great",
            "Search Engines are great",
            "Google is a nice search engine");

        Map<String, Integer> result = terms.parallelStream().
            flatMap(s -> Arrays.asList(s.split(" ")).stream()).
            collect(Collectors.toConcurrentMap(
                w -> w.toLowerCase(), w -> 1, Integer::sum));
        System.out.println(result);
    }
}

请注意,您可能需要考虑字符串的大写/小写是否应该起作用。这个将字符串转换为小写,并将它们用作最终映射的键。结果是:

{coding=1, a=1, search=2, are=1, engine=1, engines=1, 
     is=2, google=1, great=2, nice=1}

【讨论】:

  • 对于我们这些落后于技术(JDK 7)的人,您是如何做到的? ;)
  • @Mohammad 那么我猜这将是一些嵌套循环。但是使用适当的实用方法,实际的 call 可能看起来有些相似。
  • 一些嵌套循环,并且可能使用 ExecutorService 来获取并行流附近的东西(例如:您提交一些 Future 来计算单词列表)。
  • @NoDataFound 当然,用 Java 7 模拟并行性可能会很麻烦:将输入列表分配给传递给 Executorservice 的一些任务,并将结果收集到 ConcurrentHashMap - I很高兴 Java 8 很好地隐藏了这一点。
【解决方案2】:
public static void main(String[] args) {
    String msg="Coding is great search Engines are great Google is a nice search engine";                   
    ArrayList<String> list2 = new ArrayList<>();
    Map map = new HashMap();
    list2.addAll((List)Arrays.asList(msg.split(" ")));
    String n[]=msg.split(" ");
    int f=0;
    for(int i=0;i<n.length;i++){
         f=Collections.frequency(list2, n[i]);
         map.put(n[i],f);
    }     
    System.out.println("values are "+map);
}

【讨论】:

    【解决方案3】:

    因为 Java 8 的答案虽然不错,但没有向您展示如何在 Java 7 中并行处理(除了默认实现与 stream 相同),这里是一个示例:

      public static void main(final String[] args) throws InterruptedException {
    
        final ExecutorService service = Executors.newFixedThreadPool(10);
    
        final List<String> terms = Arrays.asList("Coding is great", "Search Engines are great",
            "Google is a nice search engine");
    
        final List<Callable<String[]>> callables = new ArrayList<>(terms.size());
        for (final String term : terms) {
          callables.add(new Callable<String[]>() {
    
            @Override
            public String[] call() throws Exception {
              System.out.println("splitting word: " + term);
              return term.split(" ");
            }
          });
        }
    
        final ConcurrentMap<String, AtomicInteger> counter = new ConcurrentHashMap<>();
        final List<Callable<Void>> callables2 = new ArrayList<>(terms.size());
        for (final Future<String[]> future : service.invokeAll(callables)) {
          callables2.add(new Callable<Void>() {
    
            @Override
            public Void call() throws Exception {
              System.out.println("counting word");
              // invokeAll implies that the future finished it work
              for (String word : future.get()) {
                String lc = word.toLowerCase();
                // here it get tricky. Two thread might add the same word.
                AtomicInteger actual = counter.get(lc);
                if (null == actual) {
                  final AtomicInteger nv = new AtomicInteger();
                  actual = counter.putIfAbsent(lc, nv);
                  if (null == actual) {
                    actual = nv; // nv got added.
                  }
                }
                actual.incrementAndGet();
              }
              return null;
            }
          });
        }
        service.invokeAll(callables2);
        service.shutdown();
    
        System.out.println(counter);
    
      }
    

    是的,Java 8 简化了工作!

    不,我测试了它,但不知道它是否比简单循环更好,也不知道它是否完全线程安全。

    (看看你是如何定义你的列表的,不是在 Groovy 中编码吗?在 Groovy 中存在并行支持)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-08-20
      • 2021-07-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-20
      • 1970-01-01
      相关资源
      最近更新 更多