按 java 中的 Header 列对二维列表进行排序
Sort 2D List by Column Header in java
我正在做一个简单的数据框,可以读写 CSV,并包含按列排序的排序功能。如何通过输入列 Header 对正确的列进行排序,并从排序中排除列 Header 行?
这是 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]]
我想通过传入列 Header 对正确的列进行排序,而列 Header 行不包括在排序中。
例如:
COLUMNDATA.sort(“Age”)
所以会变成这样:
Name,Age,Salary
Roger,10,4000
Mah,19,2500
Lim,20,2000
Tan,20,3000
我用过Comparator
和Collections.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);
}
你做对了一切(除了变量名不应该全部大写)。
排序前只删除第一个元素。然后排序,把表头加回列表:
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));
}
};
}
这里是如何按照你的要求去做的。定义比较器后,只需从列表 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);
}
下面是我建议您组织数据和进行排序的方式。
首先创建一个class如图。然后使用数据用 class 的实例填充列表。然后只需指定 getter 进行排序。您可以根据需要添加任意数量的附加字段及其 getter。
原因是它允许混合类型存储在同一个object中并且仍然被排序。如果您对 String number
进行排序,它将对 lexcally
而不是 numerically
进行排序。这将是一个问题,除非您转换为整数(要查看此内容,请将 4000
更改为 400
并按上面的薪水排序)。但是如果你想按名称排序,你需要一个不同的比较器,因为将 non-int 转换为 int 会抛出异常。这一切都可以在某种程度上得到缓解,但它不像创建 class.
那样直接
只需将方法引用更改为所需的 getter
,您就可以对任何字段的 List
进行排序。如果不存在 getter,并且字段为 public(不推荐),您可以使用 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
您可以将此列表的一部分 sorted
starting from the second row, and then 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]
在这种情况下,二维数组比二维列表更有用,这样你就可以使用 Arrays.sort(T[],int,int,Comparator)
方法对从 index 到 index 的特定范围进行排序:
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]
我建议不要用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;
}
}
}
您可以使用List.subList(int,int)
method to get the portion of this list which is backed by this list between the specified indices, and then use 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]
另请参阅:
•
•
我正在做一个简单的数据框,可以读写 CSV,并包含按列排序的排序功能。如何通过输入列 Header 对正确的列进行排序,并从排序中排除列 Header 行?
这是 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]]
我想通过传入列 Header 对正确的列进行排序,而列 Header 行不包括在排序中。 例如:
COLUMNDATA.sort(“Age”)
所以会变成这样:
Name,Age,Salary
Roger,10,4000
Mah,19,2500
Lim,20,2000
Tan,20,3000
我用过Comparator
和Collections.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);
}
你做对了一切(除了变量名不应该全部大写)。
排序前只删除第一个元素。然后排序,把表头加回列表:
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));
}
};
}
这里是如何按照你的要求去做的。定义比较器后,只需从列表 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);
}
下面是我建议您组织数据和进行排序的方式。
首先创建一个class如图。然后使用数据用 class 的实例填充列表。然后只需指定 getter 进行排序。您可以根据需要添加任意数量的附加字段及其 getter。
原因是它允许混合类型存储在同一个object中并且仍然被排序。如果您对 String number
进行排序,它将对 lexcally
而不是 numerically
进行排序。这将是一个问题,除非您转换为整数(要查看此内容,请将 4000
更改为 400
并按上面的薪水排序)。但是如果你想按名称排序,你需要一个不同的比较器,因为将 non-int 转换为 int 会抛出异常。这一切都可以在某种程度上得到缓解,但它不像创建 class.
只需将方法引用更改为所需的 getter
,您就可以对任何字段的 List
进行排序。如果不存在 getter,并且字段为 public(不推荐),您可以使用 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
您可以将此列表的一部分 sorted
starting from the second row, and then 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] |
[Name, Age, Salary] |
在这种情况下,二维数组比二维列表更有用,这样你就可以使用 Arrays.sort(T[],int,int,Comparator)
方法对从 index 到 index 的特定范围进行排序:
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] |
[Name, Age, Salary] |
我建议不要用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;
}
}
}
您可以使用List.subList(int,int)
method to get the portion of this list which is backed by this list between the specified indices, and then use 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] |
[Name, Age, Salary] |
另请参阅:
•
•