【问题标题】:How can I map all words in the description of a Film to all films' names that contain the word in their description fast?如何快速将电影描述中的所有单词映射到所有包含该单词的电影名称?
【发布时间】:2022-01-07 09:43:12
【问题描述】:

我有一个List<Films> films,其中每部电影都有int id 和String description。 我的任务是将所有描述中的每个单词映射到描述中包含该单词的所有电影名称,它必须是这样的:

<word1>: <filmId11>, <filmId12>,..., <filmId1N>
<word2>: <filmId21>, <filmId22>, ..., <filmId2N>
...

我使用 Java Stream API 做到了:

private List<Map.Entry<String, String>> wordToFilmIds;

private void addWordsFromDescriptions(List<Film> films) {
        for (Film film : films) {
            String description = film.description();
            String[] tokens = description.split("[\\p{IsPunctuation}\\p{IsWhite_Space}]+");
            allWords.addAll(Arrays.stream(tokens).toList());
        }
        
    }

    private void mapWordsToFilmIDs(List<Films> films) {
        wordToFilmIds = allWords.stream()
                .map(word -> Map.entry(word,
                        films.stream()
                                .filter(film -> film.description().contains(word))
                                .map(film -> String.valueOf(film.id()))
                                .collect(Collectors.joining(","))))
                .toList();

        
    }

但问题是我的解决方案太慢了,我必须处理大数字,电影的数量约为 12 000,而且描述也不短。另外,我被not 允许使用multi-threading。 知道如何优化它吗? 现在程序还没有结束。

我也尝试过使用parallel streams,但还是不行。

【问题讨论】:

  • 阅读inverted indexes,然后根据电影描述中的所有文字构建HashMap&lt;String, Set&lt;Film&gt;&gt;,您的世界将会改变。
  • 我是编程新手,还没有听说过。非常感谢!

标签: java performance java-stream


【解决方案1】:

我认为你为每个单词迭代每部电影的事实使得解决方案 O(n^2)。不过,它可以通过一次迭代来实现:

给定助手类:

public class Tuple<A,B> {
    public A a;
    public B b;
    public Tuple(A a, B b) {
        this.a = a;
        this.b = b;
    }
}

试试这个:

    Map<String, Set<Integer>> addWordsFromDescriptions(List<Film> films) {
        return films.stream()
                .flatMap(film -> tokenizeDescription(film).map(token -> new Tuple<>(token, film)))
                .collect(Collectors.groupingBy(
                        tuple -> tuple.a,
                        Collectors.mapping(tuple -> tuple.b.id(), Collectors.toSet())
                ));
    }

    private Stream<String> tokenizeDescription(Film film) {
        return Stream.of(film.description().split("[\\p{IsPunctuation}\\p{IsWhite_Space}]+"));
    }

给定Map&lt;String, Set&lt;Integer&gt;&gt;,你可以加入集合中的id,得到你想要的字符串。

【讨论】:

    【解决方案2】:
    public static class Film {
    
        private final String id;
        private final String description;
    
        public Film(String id, String description) {
            this.id = id;
            this.description = description;
        }
    
        public String getId() {
            return id;
        }
    
        public String getDescription() {
            return description;
        }
    
    }
    
    public static void main(String... args) {
        List<Film> films = List.of();
        Map<String, Film> filmById = films.stream().collect(Collectors.toMap(Film::getId, Function.identity()));
        Map<String, Set<String>> filmByLowerCaseDescriptionWord = createDescriptionMap(films);
    }
    
    private static Map<String, Set<String>> createDescriptionMap(List<Film> films) {
        Map<String, Set<String>> map = new HashMap<>();
    
        films.forEach(film -> Arrays.stream(film.getDescription().split("\\S+"))
                                    .map(word -> word.trim().toLowerCase(Locale.ROOT))
                                    .forEach(lowerCaseWord ->
                                            map.computeIfAbsent(lowerCaseWord, key ->
                                                    new HashSet<>()).add(film.getId())));
    
        return map;
    }
    

    【讨论】:

    • 非常感谢!
    【解决方案3】:

    有时,for-each 循环可能比 streams 更清晰和可读。

    Java-8 在Map 接口中引入了new 方法; computeIfAbsent 非常适合这种情况:

    Map<String, Set<Integer>> wordsToFilmIdMap = new HashMap<>();
    
    for (Film film : films) {
       String[] filmDescArray = film.getDesc().split("[\\p{IsPunctuation}\\p{IsWhite_Space}]+");
       for (String descWord : filmDescArray) {
            wordsToFilmIdMap.computeIfAbsent(descWord, unused -> new HashSet<>()).add(film.getId());
       }
    }
    

    【讨论】:

      猜你喜欢
      • 2021-08-21
      • 1970-01-01
      • 1970-01-01
      • 2019-02-18
      • 2011-06-13
      • 1970-01-01
      • 2014-09-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多