【问题标题】:Adding Upon a Value in a Linkedhashmap在 Linkedhashmap 中添加值
【发布时间】:2021-01-10 05:12:46
【问题描述】:

我正在遍历几个文本文件,并试图在所有文本文件中找到前 20 个单词。我设法设置了一些代码来查找单个文件中的前 20 个单词。但是,现在我正在为几个文件而苦苦挣扎。

我有一个全局链接哈希图,我想在其中存储我在文本文件中遇到的每个新单词(作为键),并且我想在遇到更多时更新它的值(它出现的次数)这个词的。例如,在第一个文件中,我找到了 8000 个单词“the”的实例,在下一个文件中,我在另一个文件中遇到了 7000 个“the”实例,然后我希望将键“the”的值更新为 15000 .

这是我的代码:

import java.util.*;
import java.util.stream.Collectors;
import java.io.IOException;
import java.nio.file.*;
import java.util.Map.Entry;
import java.util.function.Function;
import java.io.File;
import java.nio.charset.StandardCharsets;

public class FileReaderTwo
{
    static LinkedHashMap<String, Long> top20Words = null;
    public static void main(String args[])
    {
        File dir = new File("data/");
        for (File file : dir.listFiles()) 
        {
            try
            {
                top20Words = Files.lines(Paths.get(file.toString()), StandardCharsets.ISO_8859_1)
                        .flatMap(line -> Arrays.stream(line.toLowerCase().split("[\\(,\\).\\s+]+")))
                        .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())).entrySet().stream()
                        .sorted(Entry.comparingByValue(Comparator.reverseOrder()))
                        .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
                        .collect(Collectors.toMap(Entry::getKey, Entry::getValue, (u, v) -> u, LinkedHashMap::new));
            } catch (IOException e)
            {
                e.printStackTrace();
            }
        }
        System.out.println(top20Words);
    }
}

注意:我知道在它打印出每个单词的那一刻,我想先处理这个问题,然后再修复它。

【问题讨论】:

  • 确定你想要+ inside正则表达式中的字符类吗? [\(,\).\s+]+? --- 另外,括号在字符类中并不特殊,因此不需要转义。我想你的意思只是[(),.\s]+。 --- 也许您想要除'- 之外的所有非字母字符?如果是这样,请指定要保留的字符,然后否定它,例如[^\p{L}\p{N}'\-]+

标签: java sorting collections io linkedhashmap


【解决方案1】:

好的,我修改了这个来做我相信你正在寻找的东西。它的工作原理如下。

  • 创建了两种方法来帮助处理文件访问异常。我发现这不仅更简单、更简洁,而且是推荐的方法,而不是尝试在流中定位 try 类构造。
  • 获取所有单词并计算频率并将它们存储在地图中。
  • 对 map 的 entrySet 进行排序,并将前 20 个单词(字数最高)按降序排列在 map 中。

总体结果是统计多个文件中的所有单词,并按降序呈现。

import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class FileWordCount {
    
    public static void main(String[] args) {
        FileWordCount fwc = new FileWordCount();
        Map<String,Long> map = fwc.getTheWords();
        map.entrySet().forEach(System.out::println);
    }
    
    // helper methods to handle exceptions.
    private  Stream<Path> getFiles(String dir) {
        try {
            return Files.list(Path.of(dir));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
    private  Stream<String> getLines(Path path) {
        try {
            return Files.lines(path,StandardCharsets.ISO_8859_1);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
        
    
    public Map<String, Long>  getTheWords() {
        
        String dir = "f:./data";
    
        return getFiles(dir)
              .flatMap(this::getLines) 
                .flatMap(line -> Arrays.stream(
                        line.toLowerCase().split("[\\(,\\).\\s+]+")))
                .collect(Collectors.groupingBy(word -> word,
                        Collectors.counting())) 
                .entrySet().stream() 
                .sorted(Entry.<String,Long>comparingByValue().reversed().
                        thenComparing(Entry.<String,Long>comparingByKey()))
                .limit(20) // limts the number of entries
                .collect(Collectors.toMap(Entry::getKey, Entry::getValue,
                        (r,u)->r,
                        LinkedHashMap::new));

    }
}

注意。我首先按倒数排序,然后,如果有平局,我按正常顺序按字母顺序排序。

【讨论】:

  • 感谢您的回复。但是,我不知道如何将其分类为一个奇异的 LinkedHashMap (我是这类东西的新手)。这似乎是 > 格式。我试图只拥有一个格式为 的 LinkedHashMap,其中 String 是单词,Long 是它在所有文件中一起出现的次数。
【解决方案2】:

首先,不要将旧的File API 与新的 NIO.2 API 混合使用。

您可以从 Stream 个文件开始合并所有文件的结果。

Path dir = Paths.get("data/");
LinkedHashMap<String, Long> top20Words = Files.list(dir)
    .filter(path -> ! Files.isDirectory(path))
    .flatMap(file -> {
        try {
            return Files.lines(file, StandardCharsets.ISO_8859_1);
        } catch (IOException e) {
            e.printStackTrace();
            return Stream.empty();
        }
    })
    // the rest is copied from question, to show context
    .flatMap(line -> Arrays.stream(line.toLowerCase().split("[\\(,\\).\\s+]+")))
    .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())).entrySet().stream()
    .sorted(Entry.comparingByValue(Comparator.reverseOrder()))
    .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
    .collect(Collectors.toMap(Entry::getKey, Entry::getValue, (u, v) -> u, LinkedHashMap::new));
System.out.println(top20Words);

【讨论】:

  • 这会将所有文件变成一个单一的流吗?我不确定这是否是我正在寻找的,因为我最终希望实现线程,每个线程将处理一个文件并更新全局 LinkedHashMap。
  • @MikolasSlama 然后添加.parallel() 例如在filter() 调用之前,每个文件将由一个单独的线程处理。当最后一个文件被完整处理后,collect() 调用将返回唯一的“全局”LinkedHashMap。
  • 我将不得不研究并行()。我是新手,所以如果我对给定答案提出太多问题,我深表歉意。当我尝试运行您提供的代码时,它不会编译。它说即使您提供了 try-catch,Files.list(dir) 也存在未报告的异常。
  • 不需要二次排序。而且我认为,转换较小的字符串更便宜,即删除split之前的toLowerCase(),并在groupingBy收集器中将Function.identity()替换为String::toLowerCase
  • @Andreas 我知道这不是答案的重点。但是,当我对问题发表评论并且 OP 删除了多余的 sorted 时,您的答案会突然看起来好像您添加了一个 sorted 步骤,让读者感到困惑。因此,我更喜欢将评论留在复制代码的提问者和回答者都能收到通知的地方。一般来说,我认为,即使不是问题的重点,也值得指出明显的问题。你会告诉那些问“这条路通向……”的人一个简单的“是的,有”,因为“他们没有问这座桥是否安全……”?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多