当前位置: 首页 > 知识库问答 >
问题:

如何按Java中的日期对ArrayList的元素进行排序?

羿昊英
2023-03-14

我创建了一个To-Do-List程序,记录用户输入的任务。对于每个任务,用户必须输入名称、日期等。

当用户从菜单中选择“5”时,程序会按这些任务的日期对它们进行排序。我需要根据任务的日期和时间的升序对所有任务进行排序,即日期和时间较早的任务会排在日期和时间较晚的任务之前,并显示排序后的列表。

然而,当我运行代码时,我会收到以下错误:

Exception in thread "main" java.lang.IllegalArgumentException: Cannot format given Object as a Date
at java.base/java.text.DateFormat.format(DateFormat.java:338)
at java.base/java.text.Format.format(Format.java:158)
at ToDoList.lambda$0(ToDoList.java:238)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
at ToDoList.sortTasks(ToDoList.java:238)
at ToDoList.main(ToDoList.java:106)

下面是我到目前为止的代码:(sorttasks()在底部)

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner; 

class Task{

private String theTitle;
private Date theDate;
private String theTime;
private String theLocation;
private String theDuration;
private String theCategory;
SimpleDateFormat format=new SimpleDateFormat("dd/MM/yyyy");

Task(String title, Date date, String time, String location, String duration, String category) {
    
    theTitle = title;
    theDate = date;
    theTime = time;
    theLocation = location;
    theDuration = duration;
    theCategory = category;
    
}

public String getTitle() {
    
    return theTitle;
}

public Date getDate() {

    return theDate;
}

public String getTime() {
    
    return theTime;
}

public String getLocation() {
    
    return theLocation;
}

public String getDuration() {
    
    return theDuration;
}

public String getCategory() {
    
    return theCategory;
}

public String getItem() {
    
    return theTitle + ", " + format.format(theDate) + ", " + theTime + ", " + theLocation + ", " + theDuration + ", " + theCategory;
}

}


public class ToDoList {

public Task myTaskObj;
SimpleDateFormat format=new SimpleDateFormat("dd/MM/yyyy");
private static List<String> currentList = new ArrayList<String>();

public ToDoList() {
    
}

public static void main (String[] args) throws ParseException {
    
    ToDoList listObj = new ToDoList();
    
    int menuItem = -1;
    while (menuItem != 7) {
        menuItem = listObj.printMenu();
        switch (menuItem) {
        case 1:
            listObj.addItem();
            break;
        case 2:
            listObj.removeItem();
            break;
        case 3:
            listObj.removeAllTasks();
            break;
        case 4:
            listObj.showList();
            break;
        case 5: 
            listObj.sortTasks();
            break;
        case 6:
            listObj.searchTasks();
            break;
        case 7: 
            System.out.println("Goodbye!");
        default:
            System.out.println("Enter a valid option");
        }
    }   
    
}

public int printMenu() {
    
    Scanner scanner = new Scanner(System.in);
    System.out.println();
    System.out.println("----------------------");
    System.out.println("Main Menu");
    System.out.println("----------------------");
    System.out.println("1. Add a task");
    System.out.println("2. Delete a task");
    System.out.println("3. Delete all tasks");
    System.out.println("4. List all tasks");
    System.out.println("5. Sort tasks by date");
    System.out.println("6. Search for a task");
    System.out.println("7. Exit the program");
    System.out.println();
    System.out.print("Enter choice: ");
    int choice = scanner.nextInt();
    
    return choice;
    
}

public void showList() {
System.out.println();
System.out.println("----------------------");       
System.out.println("To-Do List");
System.out.println("----------------------");
int number = 0;
for (String item : currentList) {
    System.out.println(++number + ". " + item);
}
System.out.println("----------------------");


}

public void addItem() throws ParseException {
System.out.println("Add a task");
System.out.println("----------------------");

System.out.print("Enter the task title: ");
Scanner scanner = new Scanner(System.in);
String title = scanner.nextLine();

System.out.print("Enter the task date (dd/mm/yyyy): ");
Scanner scanner2 = new Scanner(System.in);
Date date=format.parse(scanner2.next());


System.out.print("Enter the task time: ");
Scanner scanner3 = new Scanner(System.in);
String time = scanner3.nextLine();

System.out.print("Enter the task location: ");
Scanner scanner4 = new Scanner(System.in);
String location = scanner4.nextLine();

System.out.println("Enter the task duration (optional - press enter to skip): ");
Scanner scanner5 = new Scanner(System.in);
String duration = scanner5.nextLine();

System.out.println("Enter the task category (optional - press enter to skip): ");
Scanner scanner6 = new Scanner(System.in);
String category = scanner6.nextLine();

myTaskObj = new Task(title, date, time, location, duration, category);

String theItem = myTaskObj.getItem();

currentList.add(theItem);
System.out.println("Task Added!");



}

public void removeItem() {
System.out.println("Delete a task");
System.out.println("----------------------");
Scanner scanner = new Scanner(System.in);
System.out.print("What do you want to remove? (Enter number): ");
int index = scanner.nextInt();
if((index-1)<0 || index>currentList.size()) {
    System.out.println("Wrong index number! Please enter in range of 1 to "+currentList.size());            
}else {
    currentList.remove(index-1);
}
System.out.println("----------------------");
System.out.println("Task Removed!");


}

public void removeAllTasks() {

System.out.println("Remove all tasks");
System.out.println("----------------------");
showList();

Scanner keyboard = new Scanner(System.in);
System.out.print("Are you sure you'd like to delete all tasks? 'Yes' or 'No': ");
String choice = keyboard.nextLine();
if(choice.equals("Yes")) {
    currentList.removeAll(currentList);
    System.out.println("All tasks deleted!");
}
else 
    if(choice.equals("No"))
    System.out.println("Tasks not deleted");

}

public void sortTasks() {

System.out.println("Sorted tasks by date (earliest first): ");

Collections.sort(currentList);

currentList.forEach(action-> System.out.println(format.format(action)));
}

}

共有1个答案

轩辕华辉
2023-03-14

首先,组织代码,使其更容易处理。将每个类放在它自己的.文件中。使用适当的缩进来显示代码的层次结构。您的IDE可以在这方面提供帮助。

并记住关注点的分离。您的TodoList类应该专注于维护关于Task对象列表的有效状态。Todolist类不应了解与控制台上的用户交互的任何信息。对于该用户交互,创建一个单独的类。

查看task类,您不应该使用遗留的类java.util.datejava.sql.dateSimpleDateFormat。几年前,JSR310被java.time类所取代。暂时使用instant。对于没有时间和时区的仅日期值,请使用localdate。要解析/生成表示这些值的文本,请使用DateTimeFormatter

为了预订未来的约会等等,我们必须将日期和一天中的时间与时区分开存储。政客们经常改变他们管辖的时区所使用的偏移。因此,明年1月23日下午3点可能不是我们现在预期的那个时候。

因此Task类需要一对成员字段:LocalDateTime表示日期和时间,加上一个ZoneId时区对象。我假设您的意思是任务应该在何时开始,因为您还有一个选项duration字段。

谈到持续时间,Java有一个类,duration。它表示与时间线无关的时间跨度,以24小时-通用-天、小时、分钟和小数秒为刻度。

不应在task类上硬编码格式化程序。相反,使用task对象的调用方法应将locale对象与formatstyle对象一起传递,以自动本地化日期-时间值的显示。更好的是,人们可以认为生成格式化的日期-时间字符串甚至不应该是task类的工作。task对象应该只返回预期任务开始的预计时间,通过将存储的ZoneId对象应用到存储的LocalDateTime对象,返回ZonedDateTime对象。

下面是将ZoneID应用于LocalDateTime的方法,以ZonedDateTime对象的形式确定一个时刻(时间线上的一点)。

public ZonedDateTime projectedStartingMoment ( )
{
    ZonedDateTime zdt = this.startDateTime.atZone( this.zoneId );
    return Objects.requireNonNull( zdt );
}

这个即时生成的zoneddatetime对象也是我们对这些任务进行排序所需要的,这也是您问题的最初目的。要对task对象进行排序,我们将实现comparable接口,这需要我们编写一个compareto方法。在我们的compareTo中,我们生成ZonedDateTime对象,并对其进行比较以进行排序。不同的任务可能具有不同的时区,因此我们不能仅仅比较存储的localdatetime对象。

// Implement `Comparable` interface.
@Override
public int compare ( Task task1 , Task task2 )
{
    return task1.projectedStartingMoment().compareTo( task2.projectedStartingMoment() );
}

这里有一个表格可以帮助您保持这些不同的日期-时间类型。

我们忽略location和category字段,因为它们与按日期排序的问题无关。

我们需要能够明确区分一个task和另一个task。在实际工作中,数据库可能会使用一个主键来跟踪每个任务记录。通常,这样的主键要么是一个串行整数,要么是一个UUID。这里我们使用一个UUID。我们重写object::equalsobject::hashcode以使用此UUID标识符值。我们的task对象的sortedset集合可能会使用这些方法。

不需要在成员字段前面加上

以便task类看起来如下所示。顺便说一下,Java 15中的新记录功能可以用于这个类,但我们不会在这里做,因为Java 15还没有发布。

package work.basil.example;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.Comparator;
import java.util.Objects;
import java.util.UUID;

public class Task
        implements Comparable < Task >
{

    // Member fields.
    private UUID id;
    private String title;
    private LocalDateTime startDateTime;
    private ZoneId zoneId;
    private Duration duration;

    // Constructor
    public Task ( UUID id , String title , LocalDateTime startDateTime , ZoneId zoneId , Duration duration )
    {
        this.id = id;
        this.title = title;
        this.startDateTime = startDateTime;
        this.zoneId = zoneId;
        this.duration = duration;
    }

    // Logic

    public ZonedDateTime projectedStartingMoment ( )
    {
        ZonedDateTime zdt = this.startDateTime.atZone( this.zoneId );
        return Objects.requireNonNull( zdt );
    }

    public ZonedDateTime projectedEndingMoment ( )
    {
        ZonedDateTime zdt = this.startDateTime.atZone( this.zoneId ).plus( this.duration );  // Half-Open approach, for spans-of-time that neatly abut one another without gaps.
        return Objects.requireNonNull( zdt );
    }

    // Accessors
    // Getters only, immutable object.

    public UUID getId ( ) { return this.id; }

    public String getTitle ( ) { return this.title; }

    public LocalDateTime getStartDateTime ( ) { return this.startDateTime; }

    public ZoneId getZoneId ( ) { return this.zoneId; }

    public Duration getDuration ( ) { return this.duration; }


    // Object overrides.


    @Override
    public String toString ( )
    {
        return "Task{ " +
                "id=" + id +
                " | title='" + title + '\'' +
                " | startDateTime=" + startDateTime +
                " | zoneId=" + zoneId +
                " | duration=" + duration +
                " | projectedStartingMoment=" + projectedStartingMoment() +
                " }";
    }

    @Override
    public boolean equals ( Object o )
    {
        if ( this == o ) return true;
        if ( o == null || getClass() != o.getClass() ) return false;
        Task task = ( Task ) o;
        return getId().equals( task.getId() );
    }

    @Override
    public int hashCode ( )
    {
        return Objects.hash( getId() );
    }

    @Override
    public int compareTo ( Task other )
    {
        return this.projectedStartingMoment().compareTo( other.projectedStartingMoment() );
    }
}

接下来,我们将Task类的对象收集到ToDoList中。您的问题中的Todolist类混合了关注点、处理用户交互和处理表示。这两个都属于你的应用程序类。可以这样想,如果您稍后在控制台用户界面之外向应用程序添加GUI,您的TODOLIST::ShowList将是不合适的,不相关的。这告诉我们“showlist”工作不属于Todolist类。

此时,我们的TodoList类可以只是ListSet,而不需要定义自己的类。但在实际工作中,这个班可能会有额外的任务。因此我们将继续创建该类。

package work.basil.example;

import java.util.*;

public class ToDoList
{
    private SortedSet < Task > tasks;


    // Constructors

    public ToDoList ( )
    {
        this.tasks = new TreeSet <>();
    }

    public ToDoList ( Collection < Task > tasks )
    {
        this(); // Call other constructor
        this.tasks.addAll( tasks );
    }

    // Logic

    public boolean addTask ( Task task )
    {
        Objects.requireNonNull( task ); // Fail fast. In real work, pass a message for the exception.
        boolean result = this.tasks.add( task );
        return result;
    }

    public boolean addTasks ( Collection tasks )
    {
        return this.tasks.addAll( Objects.requireNonNull( tasks ) );
    }

    public boolean remove ( Task task )
    {
        return this.tasks.remove( Objects.requireNonNull( task ) );
    }

    public void removeAll ( )
    {
        this.tasks.clear();
    }

    public List < Task > getTasksSortedByProjectedStartingMoment ( )
    {
        // Make a copy of our `SortedSet`, to be separate from our own here.
        // This way the calling method can do what they want, as can this class,
        // while not stepping on each other's feet.
        Objects.requireNonNull( this.tasks ); // Paranoid check.
        return List.copyOf( this.tasks );
    }
}

让我们驾驭那些类,看看他们的行动。我将使用您的新app(main)类的开始部分在控制台与用户交互。但我不会做所有的用户交互代码,因为这与问题无关。这里我只是实例化了几个task对象,将它们放入一个Todolist中,然后返回一个按日期排序的列表。

为了最终回答您的问题,我们调用ToList::GetTasksSortedByProjectedStartingMoment方法

List < Task > tasksSorted = this.toDoList.getTasksSortedByProjectedStartingMoment();
    

完整的示例代码。

package work.basil.example;

import java.time.Duration;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.ZoneId;
import java.util.List;
import java.util.UUID;

public class ToDoListEditorConsole
{
    private ToDoList toDoList;

    public static void main ( String[] args )
    {
        ToDoListEditorConsole app = new ToDoListEditorConsole();
        app.launch();
    }

    private void launch ( )
    {
        this.toDoList = new ToDoList();
        this.demo();
    }

    private void demo ( )
    {
        // Make a few `Task` objects. All on the same day in the same zone, but different time-of-day.
        // Notice that our time-of-day values are *not* in chronological order.
        List < Task > tasks = List.of(
                new Task(
                        UUID.fromString( "98399344-bb31-11ea-b3de-0242ac130004" ) ,
                        "Eat apple" ,
                        LocalDateTime.of( 2021 , Month.JANUARY , 23 , 12 , 30 , 0 , 0 ) ,
                        ZoneId.of( "Africa/Tunis" ) , Duration.ofHours( 1 )
                ) ,
                new Task(
                        UUID.fromString( "1e4ded04-bb32-11ea-b3de-0242ac130004" ) ,
                        "Eat banana" ,
                        LocalDateTime.of( 2021 , Month.JANUARY , 23 , 20 , 00 , 0 , 0 ) ,
                        ZoneId.of( "Africa/Tunis" ) , Duration.ofHours( 1 )
                ) ,
                new Task(
                        UUID.fromString( "010fcde8-bb32-11ea-b3de-0242ac130004" ) ,
                        "Eat corn" ,
                        LocalDateTime.of( 2021 , Month.JANUARY , 23 , 15 , 00 , 0 , 0 ) ,
                        ZoneId.of( "Africa/Tunis" ) , Duration.ofMinutes( 30 )
                )

        );
        this.toDoList.addTasks( tasks );

        List < Task > tasksSorted = this.toDoList.getTasksSortedByProjectedStartingMoment();
        System.out.println( "Result:" );
        System.out.println( tasksSorted );
        System.out.println( "« fin »" );
    }
}

运行时,注意香蕉和玉米任务(第2和第3)是如何转换位置的,现在按时间顺序排序。

结果:

[Task{id=98399344-BB31-11EA-B3DE-0242AC130004 title='吃苹果‘startdatetime=2021-01-23T12:30 ZoneID=Africa/Tunis Duration=PT1H ProjectedStartingMoment=2021-01-23T12:30+01:00[非洲/突尼斯]},Task{id=010FCDE8-BB32-11EA-B3DE-0242AC130004 title='吃玉米’startdatetime=2021-01-23T15:00 ZoneID=Africa/Tunis Duration=PT30M

“鳍”

 类似资料:
  • 问题内容: 我发现的每个示例都是按字母顺序进行的,而我需要按日期对元素进行排序。 我的ArrayList包含其数据成员之一是DateTime对象的对象。在DateTime上,我可以调用以下函数: 因此,我可以做一些比较: 我应该在if块内做什么? 问题答案: 你可以使对象具有可比性: 然后通过调用以下命令对其进行排序: 但是,有时你不想更改模型,例如想要对几个不同的属性进行排序时。在这种情况下,你

  • 因此,我有一个,下面的字符串作为元素: null null

  • 问题内容: 我有一个数组,如: 谁能建议一种基于datetime元素对此进行排序/排序的方法? 问题答案: 使用和自定义比较功能: 编辑 :您的数据组织在一个数组的数组中。为了更好地区分这些数据,我们将其称为内部数组(数据)记录,以便您的数据实际上是一个记录数组。 会一次将其中两个记录传递给给定的比较函数。 然后将每个记录的字段提取为UNIX时间戳(整数),并返回差值,以便结果将是两个日期相等,如

  • 问题内容: 我在Java中有一个双打列表,我想按降序对ArrayList进行排序。 输入如下: 输出应该是这样的 问题答案: 那会做你想要的。请记住要导入!

  • 问题内容: 我有一个包含一个称为的对象的数组,它具有诸如“日期”,“名称”等属性。 我正在对数组进行排序,如下所示: 应该将日期从新到旧排序。例如: 2016年6月30日 2016年6月29日 等等.. 但是,当我的数组包含“ 2016年7月2日”时,排序后的数组将变为: 2016年6月30日 2016年6月29日 2016年7月2日 排序后,“ 2016年7月2日”应该位于顶部,而现在位于底部?

  • 问题内容: 我有一堂水果课。我正在创建此类的列表,并将每个水果添加到列表中。我想根据水果名称的顺序对该列表进行排序。 我正在使用for循环创建它的列表 我需要使用列表中每个对象的水果名称对该arrayList进行排序 问题答案: 使用这样的: 现在,你的水果清单将基于进行排序。