我创建了一个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)));
}
}
首先,组织代码,使其更容易处理。将每个类放在它自己的.
文件中。使用适当的缩进来显示代码的层次结构。您的IDE可以在这方面提供帮助。
并记住关注点的分离。您的TodoList
类应该专注于维护关于Task
对象列表的有效状态。Todolist
类不应了解与控制台上的用户交互的任何信息。对于该用户交互,创建一个单独的类。
查看task
类,您不应该使用遗留的类java.util.date
、java.sql.date
和SimpleDateFormat
。几年前,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::equals
和object::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
类可以只是List
或Set
,而不需要定义自己的类。但在实际工作中,这个班可能会有额外的任务。因此我们将继续创建该类。
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进行排序 问题答案: 使用这样的: 现在,你的水果清单将基于进行排序。