【问题标题】:Sort 2D List by Column Header in java在java中按列标题对二维列表进行排序
【发布时间】:2021-03-29 11:03:45
【问题描述】:

我正在做一个简单的数据框,它可以读写 CSV,并包含按列排序的排序功能。如何通过输入列标题对正确的列进行排序,并从排序中排除列标题行?

这是 CSV 文件的示例数据:

Name,Age,Salary
Lim,20,2000
Tan,20,3000
Mah,19,2500
Roger,10,4000

我已经声明了我的 2D 列表,数据将如下所示:

List<List<String>> COLUMNDATA = new ArrayList();
COLUMNDATA = [[Name, Age, Salary], [Lim, 20, 2000], [Tan, 20, 3000], [Mah, 19, 2500], [Roger, 10, 4000]]

我想通过传入Column Header对正确的列进行排序,而Column Header行不包含在排序中。 例如:

COLUMNDATA.sort(“Age”)

所以它会变成这样:

Name,Age,Salary
Roger,10,4000
Mah,19,2500
Lim,20,2000
Tan,20,3000

我用过ComparatorCollections.sort,现在卡住了。如何实现我想要的功能?

final Comparator<List<String>> comparator = new Comparator<List<String>>() {
    @Override
    public int compare(List<String> object1, List<String> object2) {
        return object1.get(1).compareTo(object2.get(1));
    }
};

Collections.sort(COLUMNDATA, comparator);
for (List<String> list : COLUMNDATA) {
    System.out.println(list);
}

【问题讨论】:

    标签: java list sorting columnsorting


    【解决方案1】:

    这是您需要的方法。定义比较器后,只需从列表 1 开始对 sublist 进行排序,跳过标题。由于它是原始列表的视图,它仍然对所需项目进行排序。

    首先在要排序的字段上制作字段映射。如果你愿意,你可以不区分大小写。对于这个例子,大小写很重要。

    static Map<String, Integer> sortingFields = new HashMap<>();
    static {
        List<String> columns = List.of("Name", "Age", "Salary");
        for (int i = 0; i < columns.size(); i++) {
            sortingFields.put(columns.get(i), i);
        }
    }
    

    创建列表列表。

    List<List<String>> data = new ArrayList<>();
    data.add(new ArrayList<>(List.of("Name" ,"Age", "Salary")));
    data.add(new ArrayList<>(List.of("Lim", "20", "4000")));
    data.add(new ArrayList<>(List.of("Tan",   "20", "3000")));
    data.add(new ArrayList<>(List.of("Mah",   "19", "2500")));
    data.add(new ArrayList<>(List.of("Roger", "10", "3500")));
    

    现在调用排序和打印

    sort("Age", data);
    data.forEach(System.out::println);
    

    打印

    [Name, Age, Salary]
    [Roger, 10, 3500]
    [Mah, 19, 2500]
    [Lim, 20, 4000]
    [Tan, 20, 3000]
    

    这是排序方法。

    public static void sort(String Column, List<List<String>> data) {
            // use the column string to select the column number to sort.
            Comparator<List<String>> comp =
                    (a, b) -> a.get(sortingFields.get(column))
                            .compareTo(b.get(sortingFields.get(column)));
    
      data.subList(1,data.size()).sort(comp);
    }
    
    
    

    以下是我建议您组织数据并进行排序的方法。

    首先创建一个如图所示的类。然后使用数据用类的实例填充列表。然后只需指定要排序的 getter。您可以根据需要添加任意数量的其他字段及其 getter。

    原因是它允许混合类型存储在同一个对象中并且仍然可以排序。如果您按String number 排序,它将排序lexcally 而不是numerically。除非您转换为整数,否则这将是一个问题(要查看此内容,请将4000 更改为400 并按上面的薪水排序)。但是,如果要对名称进行排序,则需要一个不同的比较器,因为将非 int 转换为 int 会引发异常。这一切都可以在一定程度上得到缓解,但不如创建一个类那么简单。

    只需将方法引用更改为所需的getter,您就可以在任何字段上对List 进行排序。如果没有 getter,并且该字段是公共的(不推荐),您可以使用 lambda。

    public class SortingByColumn {
        
        public static void main(String[] args) {
            
            List<Person> data = new ArrayList<>();
            data.add(new Person("Lim", 20, 2000));
            data.add(new Person("Tan", 20, 3000));
            data.add(new Person("Mah", 19, 2500));
            data.add(new Person("Roger", 10, 4000));
            
            List<Person> sorted = data.stream()
                    .sorted(Comparator.comparing(Person::getAge))
                    .collect(Collectors.toList());
            System.out.printf("%10s  %10s  %10s%n", "Name","Age","Salary");
            sorted.forEach(System.out::println);
        }
        
        static class Person {
            private String name;
            private int age;
            private int salary;
            
            public Person(String name, int age, int salary) {
                this.name = name;
                this.age = age;
                this.salary = salary;
            }
            
            public String getName() {
                return name;
            }
            
            public int getAge() {
                return age;
            }
            
            public int getSalary() {
                return salary;
            }
            
            @Override
            public String toString() {
                return String.format("%10s  %10s  %10s", name, age,
                        salary);
            }
        }
    }
    

    打印

          Name         Age      Salary
         Roger          10        4000
           Mah          19        2500
           Lim          20        2000
           Tan          20        3000
    

    【讨论】:

      【解决方案2】:

      您可以从第二行开始创建此列表的一部分sorted,然后从该列表中创建collect 一个新列表,如下所示:

      public static void main(String[] args) {
          List<List<String>> columnData = List.of(
                  List.of("Name", "Age", "Salary"),
                  List.of("Lim", "20", "2000"),
                  List.of("Tan", "20", "3000"),
                  List.of("Mah", "19", "2500"),
                  List.of("Roger", "10", "4000"));
      
          List<List<String>> sortedData1 = sortByColumn(columnData, "Age");
          List<List<String>> sortedData2 = sortByColumn(columnData, 2);
      }
      
      public static List<List<String>> sortByColumn(List<List<String>> list,
                                                    String name) {
          // finding index of column by name
          int index = IntStream.range(0, list.get(0).size())
                  .filter(i -> list.get(0).get(i).equals(name))
                  .findFirst()
                  .getAsInt();
          // sorting by index
          return sortByColumn(list, index);
      }
      
      public static List<List<String>> sortByColumn(List<List<String>> list,
                                                    int index) {
          // preparing a new sorted list
          List<List<String>> sorted = new ArrayList<>(list.size());
          // header row
          sorted.add(list.get(0));
          // other rows, sorting by a specific column
          sorted.addAll(list.stream().skip(1)
                  .sorted(Comparator.comparing(row -> row.get(index)))
                  .collect(Collectors.toList()));
          return sorted;
      }
      
      sortedData1 sortedData2
      [Name, Age, Salary]
      [Roger, 10, 4000]
      [Mah, 19, 2500]
      [Lim, 20, 2000]
      [Tan, 20, 3000]
      [Name, Age, Salary]
      [Lim, 20, 2000]
      [Mah, 19, 2500]
      [Tan, 20, 3000]
      [Roger, 10, 4000]

      在这种情况下,使用 2D-array 而不是 2D-list 更有用,这样您就可以对 中的特定范围进行排序indexindex 使用Arrays.sort(T[],int,int,Comparator) 方法:

      List<List<String>> columnData = List.of(
              List.of("Name", "Age", "Salary"),
              List.of("Lim", "20", "2000"),
              List.of("Tan", "20", "3000"),
              List.of("Mah", "19", "2500"),
              List.of("Roger", "10", "4000"));
      
      String[][] arr = columnData.stream()
              .map(list -> list.toArray(String[]::new))
              .toArray(String[][]::new);
      
      Arrays.sort(arr, 1, arr.length, Comparator.comparing(row -> row[1]));
      
      Original list Sorted array
      [Name, Age, Salary]
      [Lim, 20, 2000]
      [Tan, 20, 3000]
      [Mah, 19, 2500]
      [Roger, 10, 4000]
      [Name, Age, Salary]
      [Roger, 10, 4000]
      [Mah, 19, 2500]
      [Lim, 20, 2000]
      [Tan, 20, 3000]

      【讨论】:

        【解决方案3】:

        你做的一切都是正确的(除了不应该全部大写的变量名)。

        在排序之前删除第一个元素。然后排序,并将表头添加回列表:

        List<String> header = columnData.get(0);
        columnData.remove(0);
        columnData.sort(getComparator("Age", header));
        columnData.add(0, header);
        

        如何将列号传递给比较器:

        private Comparator<List<String>> getComparator(String column,
                                                       List<String> header) {
            int index = header.indexOf(column);
            return new Comparator<List<String>>() {
                @Override
                public int compare(List<String> object1, List<String> object2) {
                    return object1.get(index).compareTo(object2.get(index));
                }
            };
        }
        

        【讨论】:

          【解决方案4】:

          我提议不要使用List,我认为将class 与相对名称一起使用更清楚。在这个类中,您可以定义所需的比较器。

          public class Foo {
              public static void main(String... args) throws IOException {
                  List<DataLine> data =
                          readFile(Path.of("e:/data.csv"), StandardCharsets.UTF_8);
                  List<DataLine> sortedByName = DataLine.Field.NAME.sort(data);
                  List<DataLine> sortedByAge = DataLine.Field.AGE.sort(data);
                  List<DataLine> sortedBySalary = DataLine.Field.SALARY.sort(data);
              }
          
              public static List<DataLine> readFile(Path path, Charset charset)
                      throws IOException {
                  try (Scanner scan = new Scanner(path, charset)) {
                      scan.useDelimiter("[,\n]");
                      scan.nextLine();    // skip header
          
                      List<DataLine> data = new ArrayList<>();
          
                      while (scan.hasNext()) {
                          String name = scan.next();
                          int age = scan.nextInt();
                          int salary = scan.nextInt();
                          data.add(new DataLine(name, age, salary));
                      }
          
                      return data;
                  }
              }
          
              public static final class DataLine {
          
                  enum Field {
                      NAME(Comparator.comparing(one -> one.name)),
                      AGE(Comparator.comparingInt(one -> one.age)),
                      SALARY(Comparator.comparingInt(one -> one.salary));
          
                      private final Comparator<DataLine> comparator;
          
                      Field(Comparator<DataLine> comparator) {
                          this.comparator = comparator;
                      }
          
                      public final List<DataLine> sort(List<DataLine> data) {
                          return data.stream()
                                  .sorted(comparator)
                                  .collect(Collectors.toList());
                      }
                  }
          
                  private final String name;
                  private final int age;
                  private final int salary;
          
                  public DataLine(String name, int age, int salary) {
                      this.name = name;
                      this.age = age;
                      this.salary = salary;
                  }
              }
          }
          

          【讨论】:

            【解决方案5】:

            您可以使用List.subList(int,int) 方法获取此列表中由该列表支持的部分 在指定索引之间,然后使用Collections.sort(List,Comparator) 方法。此代码应该适用于Java 7

            List<List<String>> columnData = Arrays.asList(
                    Arrays.asList("Name", "Age", "Salary"),
                    Arrays.asList("Lim", "20", "2000"),
                    Arrays.asList("Tan", "20", "3000"),
                    Arrays.asList("Mah", "19", "2500"),
                    Arrays.asList("Roger", "10", "4000"));
            
            Collections.sort(columnData.subList(1, columnData.size()),
                    new Comparator<List<String>>() {
                        @Override
                        public int compare(List<String> o1, List<String> o2) {
                            return o1.get(1).compareTo(o2.get(1));
                        }
                    });
            
            Before sorting After sorting
            [Name, Age, Salary]
            [Lim, 20, 2000]
            [Tan, 20, 3000]
            [Mah, 19, 2500]
            [Roger, 10, 4000]
            [Name, Age, Salary]
            [Roger, 10, 4000]
            [Mah, 19, 2500]
            [Lim, 20, 2000]
            [Tan, 20, 3000]

            另见:
            Sort List<Map<String,Object>> based on value
            How do I rotate a matrix 90 degrees counterclockwise in java?

            【讨论】:

              猜你喜欢
              • 2020-10-05
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2022-01-24
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多