简体   繁体   中英

How to Sort objects by 2 fields Java

The file I am trying to read looks like this:

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

It has around 20k rows. I already got the reading the file and storing the data in an object.

        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

Object

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;
  }
};

I would like to read the file in threads and sort by year. Can anyone provide sample code on how can multithread and merge sort be implemented together?

The output that I'd like to get

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

Try this.

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());
}

Sticking with your data structure (just renaming it from the rather peculiar CartoonYear to Cartoon ), and also sticking with the good approach of using Java streams by @saka1029 (instead of manually implementing a merge sort algorithm), you could do something like this:

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);
        }
      });
  }
}

This was just quick & dirty, not code I am proud of. Your requirement to only print year and title once per group does not make the code nicer with the if-else either. But assuming you have an input file like this:

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

you will get output like this:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM