【问题标题】:How to Sort objects by 2 fields Java如何按 2 个字段对对象进行排序 Java
【发布时间】:2020-08-14 04:16:28
【问题描述】:

我要读取的文件如下所示:

1995     Pokemon    Ikue Ôtani  
1940     Tom and Jerry    William Hanna 
1995     Pokemon    voice actor2
1940     Tom and Jerry    voice actor3
2000     Cartoon      voice actor

它有大约 20k 行。我已经可以读取文件并将数据存储在一个对象中。

        in = new BufferedReader(new FileReader(cartoonsFile));
            
        ArrayList<String> voiceActors = new ArrayList<>();  
        ArrayList<Cartoon> cartoons = new ArrayList<>();    
        
        //read each line
        String line = in.readLine();
        while (line != null) {
            String[] columns = line.split("\\t");
            String year = columns[0];
            String cartoon = columns[1];
            String voiceActor = columns[2];

            //make new object and store data

            Cartoon c = new Cartoon(Integer.parseInt(columns[0]),
                                    columns[1], columns[2]));
            cartoons.add(c); //add to the array list

对象

public class Cartoon {
  private int year;
  private String title;
  private String voiceActor;

  public Cartoon(int year, String title, String voiceActor) {
    this.year = year;
    this.title = title;
    this.voiceActor = voiceActor;
  }
};

我想在线程中读取文件并按年份排序。谁能提供有关如何一起实现多线程和归并排序的示例代码?

我想得到的输出

1940     Tom and Jerry         William Hanna 
                               voice actor2
                               voice actor3
                               voice actor4
                               voice actor5

1995     Pokemon               Ikue Ôtani  
                               voice actor2

         A Cartoon             voice actor1
                               voice actor2
                               voice actor3

2000     Cartoon               voice actor

【问题讨论】:

  • 您的问题缺少信息:您的代码看起来每部电影只有一个配音演员,但您想要的输出看起来好像可能有多个配音演员。我看不到你在哪里/如何解析它。输入格式没有完整描述,我也看不到关键类CartoonYear(错误的类名,BTW)。
  • 啊,好吧,在你最后一次编辑之后,我可以看到输入文件是如何组织的,以防每部电影有多个配音演员。我认为创建一个Cartoon 类会更好,它可以有多个配音演员,而不是许多多余的配音演员。
  • 那我把 CartoonYear 改成 'Year' 类,它可以容纳多个卡通类,对吧?
  • 我不是这个意思。我宁愿考虑一个Cartoon 课程,其中包含年份、标题和一个或一组演讲者。但是根据您自己的方法和@saka1029 的回答,您也可以使用Java 流和Collectors.groupingBy。但是你需要某种按年份+标题分组的帮助类(只有年份不是唯一的)。我可以发布一个简单的例子。
  • 为什么? 20,000 个条目不是一个大文件,无论你怎么做,它都会在几纳秒内排序。不需要多线程。

标签: java multithreading data-structures filereader mergesort


【解决方案1】:

坚持你的数据结构(只是将它从相当奇特的CartoonYear 重命名为Cartoon),并坚持@saka1029 使用Java 流的好方法(而不是手动实现合并排序算法),你可以这样做:

package org.acme;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class Cartoon {
  private int year;
  private String title;
  private String voiceActor;

  public Cartoon(int year, String title, String voiceActor) {
    this.year = year;
    this.title = title;
    this.voiceActor = voiceActor;
  }

  public static Map<String, List<Cartoon>> readAndGroupByYearAndTitle(String inFile) throws IOException {
    return Files.readAllLines(Paths.get(inFile))
      .parallelStream()
      .map(line -> line.split("\\t"))
      .map(columns -> new Cartoon(Integer.parseInt(columns[0]), columns[1], columns[2]))
      .collect(Collectors.groupingBy(cartoon -> String.format("%4d %s", cartoon.year, cartoon.title)));
  }

  public static void main(String[] args) throws IOException {
    Map<String, List<Cartoon>> cartoonsGrouped = readAndGroupByYearAndTitle(args[0]);
    cartoonsGrouped.keySet()
      .parallelStream()
      .sorted()
      .forEachOrdered(group -> {
        boolean firstElement = true;
        for (Cartoon cartoonYear : cartoonsGrouped.get(group)) {
          if (firstElement) {
            System.out.printf("%4d  %-25s  %s%n", cartoonYear.year, cartoonYear.title, cartoonYear.voiceActor);
            firstElement = false;
          }
          else
            System.out.printf("%4s  %-25s  %s%n", "", "", cartoonYear.voiceActor);
        }
      });
  }
}

这只是快速而肮脏的,而不是我引以为豪的代码。您要求每组只打印一次年份和标题也不会使代码与if-else 一起更好。但假设你有一个这样的输入文件:

1995    Pokemon Ikue Ôtani
1940    Tom and Jerry   William Hanna
11  Sample  foo
1995    Pokemon voice actor2
1940    Tom and Jerry   voice actor3
2000    Cartoon voice actor
11  Sample  bar

你会得到这样的输出:

  11  Sample                     foo
                                 bar
1940  Tom and Jerry              William Hanna
                                 voice actor3
1995  Pokemon                    Ikue Ôtani
                                 voice actor2
2000  Cartoon                    voice actor

【讨论】:

  • 这很好!询问您是否也可以提供有关多线程和合并排序如何在读取文件中工作的示例是否太过分了?假设文件大到足以需要它们。
  • 是的,这绝对是要求太多,因为它不是这样工作的。你展示你自己的实现,用它解释和重现你的问题,其他人研究它并帮助你解决它。这不像“嘿,我有问题,完全免费为我解决”。
  • 合并排序是作为顺序排序算法引入的。您要并行化哪个部分?递归“除法”阶段将是微不足道的,但并没有使它变得更快。更复杂的是并行合并,尤其是 k-way 合并(不仅仅是 2-way)。另请注意,合并排序是为多个数据源发明的(可以追溯到从多个磁带读取要合并的数据时)。你说你只有一个列表文件。您必须在阅读之前先拆分它,否则阅读将是顺序的(就像我上面的示例一样)并且只能并行排序。
【解决方案2】:

试试这个。

List<CartoonYear> readAndSortByYear(String inFile) throws IOException {
    return Files.readAllLines(Paths.get(inFile))
        .parallelStream()
        .map(line -> line.split("\\t"))
        .map(columns -> new CartoonYear(Integer.parseInt(columns[0]), columns[1], columns[2]))
        .sorted(Comparator.comparing(CartoonYear::getYear))
        .collect(Collectors.toList());
}

【讨论】:

  • 您的解决方案中没有分组。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-11-05
  • 2011-10-18
  • 1970-01-01
  • 2011-05-16
相关资源
最近更新 更多