【问题标题】:Sort a String array by specific String value按特定字符串值对字符串数组进行排序
【发布时间】:2016-12-28 18:49:43
【问题描述】:

我有一个项目,其主要功能是将 .txt 文件读入字符串数组,并按每行中的特定字符串值对其进行排序。我的 .txt 文件是一份员工名单,上面有他们的姓名、薪水和经验。以下是我的示例列表:

  1. 姓名:托马斯·格林 |总工资:10 000 |经验:30 个月
  2. 姓名:安娜·朗 |工资总额:6 000 |经验:12 个月
  3. 姓名:迈克尔·霍尔斯 |工资总额:8 000 |经验:27 个月

现在我想要的是在使用 Scanner 将此列表更改为数组之后,通过名字、薪水值或经验值对该列表进行排序。我已阅读有关比较器的信息,但找不到正确的示例。如您所见,该程序要做的是跳过“名称:”值并按名字的字母顺序对其进行排序。或者跳过其他字符串并按最低工资值对列表进行排序。经验也是一样的,它应该按从最低到最高的月份数排序。

到现在为止我可以做的事情是:

import java.io.File;
import java.io.IOException;
import java.util.*;

public class SortList { 
    public static int loadInt() {
        Scanner s = new Scanner(System.in);

        if(!s.hasNextInt()) {
            s.next();
            s.nextLine();   
            return loadInt();
        }
        return s.nextInt();
    }


    public static void main (String[] args) throws IOException{
        //show the list
        String token1 = "";
        Scanner inFile1 = new Scanner (new File ("list.txt")).useDelimiter(",\\s*");

        List<String> temps = new ArrayList<String>();

        while(inFile1.hasNext()) {
            token1 = inFile1.next();
            temps.add(token1);
        }
        inFile1.close();

        String[] tempsArray = temps.toArray(new String[0]);         
        for(String s : tempsArray) {
            System.out.println(s);
        }

        //Sort the list

        System.out.println("How do you want to sort?" + "\n" + "1. By name" + "\n" + "2. By salary" + "\n" + "3. By experience");
        int b;
        b = loadInt();
        if (b == 1){
            ArrayList<String> namesList = new ArrayList<>();

            for(int i = 0; i<tempsArray.length; i++){
                namesList.add(tempsArray[i]);
            }

            Collections.sort(namesList, (name1, name5) -> name1.split(" ")[1].compareTo(name5.split(" ")[1]));

            for(String name : namesList){
                System.out.println(name);
            }

        }
        if (b == 2){
            ArrayList<String> salaryList = new ArrayList<>();

            for(int i = 0; i<tempsArray.length; i++){
                salaryList.add(tempsArray[i]);
            }

            Collections.sort(salaryList, (salary1, salary2) -> salary1.split(" ")[10].compareTo(salary2.split(" ")[10]));

            for(String salary : salaryList){
                System.out.println(salary);
            }

        }
        if (b == 3){
            ArrayList<String> experienceList = new ArrayList<>();

            for(int i = 0; i<tempsArray.length; i++){
                experienceList.add(tempsArray[i]);
            }

            Collections.sort(experienceList, (experience1, experience2) -> experience1.split(" ")[14].compareTo(experience2.split(" ")[14]));

            for(String experience : experienceList){
                System.out.println(experience);
            }
        }
    } 
}       

目前,当我输入整数值时,我的代码不会对列表进行排序。我这样做的方式是否正确?我通过互联网找到了 Collection 的示例,但它对我不起作用。

【问题讨论】:

  • 真正的问题是,在对各种排序事物进行任何操作之前,您需要一个合理的“对象”模型。从这个意义上说,dimo 的答案是绝对必须供您研究。
  • 感谢您的解释。但是如果我创建一个名为 Employee 的对象,我将如何从 txt 文件中加载列表?我是 Java 的初学者,因此我无法想象如何从 txt 文件中加载对象。
  • 哇,花了一些时间为你写了一个答案。希望这会有所帮助,因为我有一种模糊的感觉,我什至不会看到太多的赞成票......

标签: java arrays string sorting collections


【解决方案1】:

正确的做法是使用firstNamelastNamesalaryexperience 字段定义Employee 对象。然后您可以定义多个支持您不同排序选项的Comparator&lt;Employee&gt; 实例。然后您可以创建一个List&lt;Employee&gt; 并调用Collections.sort(),它将根据Comparator 定义的顺序对元素进行排序。

这个类可能看起来像这样:

public class Employee {
  private final String firstName;
  private final String lastName;
  private final int salary;
  private final int experience;

  // constructor, getters
}

您的Comparator 可能看起来像这样:

public class FirstNameComparator implements Comparator<Employee> {
  @Override
  public int compare(Employee e1, Employee e2) {
    return e1.getFirstName().compareTo(e2.getFirstName());
  }
}

或者在 Java 8 中,您可以使用方法引用,只需将 Comparator.comparing(Employee::getFirstName) 传递给 Collections.sort()Comparator 参数。

然后只需读取文件并从每一行中提取字段并从每一行构造一个Employee。如果您不确定这部分内容,本网站上有很多问题都在讨论如何从文本文件中解析数据。

【讨论】:

  • +1 以Comparator.comparing 提及 Java 8 解决方案。大多数人甚至不知道从哪里开始实施 Comparator,因此现成的解决方案非常有用。
  • 感谢您的解释,但是我应该使用构造函数创建文件还是在我的程序中实现这部分。那么最好的选择是将数据解析成可比较的字段?
  • 恐怕我不明白你的问题。类有构造函数,而不是文件;您可以在任何地方定义比较器 - 单独的文件、SortList 内的嵌套类,甚至是匿名类/lambdas。关键是将解析步骤与排序步骤分开。将文本文件解析为结构化对象,然后对这些对象进行排序。
  • @ifly6 是的,Java 8 对Comparator 进行了一些重大改进。在 Pre-Java-8-land Guava 的 OrderingComparisonChain 类具有类似的目的。
【解决方案2】:

您的算法的问题在于,当您从文件中读取时,Scanner 对象会同时读取所有内容,而 ArrayList 临时文件仅加载了 1 个元素。把token1 = inFile1.next();改成token1 = inFile1.nextLine();,其他都OK了。

【讨论】:

    【解决方案3】:

    进一步增强了来自 dimo414 的建议。如前所述,您创建了一个包含员工相关部分的类。

    那个类可以有一个类似的构造函数

    public Employee(String firstName, String lastName, int salary, int experienceInMonths)
    

    然后你会创建像这样的员工

    List<Employee> employees = new ArrayList<>();
    employees.add( new Employee ("Bilbo", "Beutlin", 5, 10) );
    

    当然,你的日期来自一个文件,你说它有类似的行

    Name: Thomas Green | Gross salary: 10 000 | Experience: 30 months
    

    那么,你所知道的:

    1. 您编写的代码逐行读取该文件
    2. 您创建一个辅助方法,该方法采用这样的一行...并为此返回一个 Employee 对象

    喜欢:

    public Employee createEmployeeFrom(String line) {
    

    在该方法中,您只需解析该字符串并提取相关信息。鉴于您的格式,您可以这样做:

    • 使用String.split("\|") ...应该将您的行分成三个子字符串
    • 那么第一个子字符串将是“姓名:Thomas Green”。在这里,您将使用 String.substring() 获取“:”之后的所有内容;给你“托马斯格林”。然后您可以假设最后一个空格之后的任何内容都是姓氏;之前的任何东西都是名字
    • 然后您对输入的部分执行类似的操作(使用 Integer.valueOf() 等方法将字符串数字转换为 int)
    • 最后,您调用 `return new Employee(firstNameParsedFromInput, ....

    事情是:你必须分离关注点。一个问题是使用 Employee 类建模您的数据;另一个问题是解析字符串以检索构成 Employee 的元素。只有当所有都运行良好时,您然后开始查看 Collections.sort() 并编写自己的 Comparator 类。

    【讨论】:

    • 感谢您的意见,我会在编写 Employee 类时考虑这种方法。
    • 不客气。请记住我的回答,一旦你达到 15 个代表和投票特权:-)
    猜你喜欢
    • 2015-11-27
    • 2014-10-28
    • 2013-06-11
    • 2021-07-26
    • 1970-01-01
    • 2012-11-15
    • 2012-10-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多