I have created a To-Do-List program where one of the actions requires the program to list all tasks input by the user in ascending order of the date that they fall on.
Output example:
Tasks listed in ascending order (earliest first):
TaskName, 20/04/2020, time, location, duration, category
TaskName, 18/07/2020, time, location, duration, category
TaskName, 09/08/2020, time, location, duration, category
TaskName, 21/12/2020, time, location, duration, category
So far, with the code that I have, the tasks that the user input all list, but they don't list in ascending order of the date of each task.
Here is my code so far:
public void sortTasks() {
System.out.println("Sorted tasks by date (earliest first): ");
Collections.sort(currentList);
currentList.forEach(System.out::println);
}
}
5 Answers 5
Your current approach to the problem makes it hard to achieve what you want. You have a list of Strings and want to parse some fragment of them and sort based on that. It is possible but you can make it much simplier. You already got a dedicated class to represent your Task
. You should keep a List
of Task
s then, not their String representations.
When you have a List<Task>
, there are couple of ways to sort it. You can either implement Comparable
in your class or use a Comparator
. You could do something like that:
currentList.sort(Comparator.comparing(Task::getDate))
Or (depending on desired order)
currentList.sort(Comparator.comparing(Task::getDate).reversed())
And then you use getItem()
only when you want to print the results (such method is usually called toString()
).
10 Comments
Task
, I receive multiple new errors all throughout my program. Here is a fiddle: paiza.io/projects/tF9oVNIvPa9yWX5b8e6LtQ?language=java List<String>
, you add a String at 182, and iterate over it as list at 144 and 246.getItem
should still return String
. The thing is you should be adding myTaskObj
to the list instead of theItem
. Then in each place you want to use it as a String
, you call getItem
on the object in there.searchTasks()
? Also, after doing these changes, the tasks are still not printing in ascending orderFirst of all you need to store Task
objects not String
in your list.
Usually you can pass a Comparator
to Collections.sort
.
Collections.sort(tasks, Comparator.reverseOrder());
In order for that to work properly you have to make Task
an implementation of Comparable
, the way you compare the fields of the object depend on your specific task, here you can provide implementation for ascending comparison, and than reverse it by reverseOrder
method.
class Task implements Comparable<Task> {
...
@Override
public int compareTo(Task task) {
return Comparator
.comparing(Task::getTitle)
.thenComparing(Task::getDate)
.compare(this, task);
}
}
Alternately, you can create and pass to sort
a more sophisticated Comparator
object, without Task
being a Comparable
object.
Note though, that this approach makes code less reusable.
Collections.sort(tasks,
Comparator
.comparing(Task::getTitle)
.thenComparing(Task::getDate)
.reverseOrder()
);
Also consider using SortedSet
or PriorityQueue
instead of List
for your task in order to avoid explicit sorting and reduce algorithmic complexity
2 Comments
You can sort List
use sort
method with custom Comparator
list.sort(Comparator.comparing(Task::getDate).reversed());
But I think you have to use another collection. PriorityQueue
feet better than ArrayList
.
Queue<Task> queue = new PriorityQueue<>(Comparator.comparing(...));
2 Comments
PriorityQueue
with custom string Comparator
Since creating TaskList and sorting based on dates solution is already provided by @Amongalen.
I will provide a different approach with better Time complexity. Since Sorting a collection can be O(nlogn). It is not a good idea to sort after every time you add an element to the list.
Instead you can maintain a PriorityQueue
of Task Object and compare them based on date and add Task
objects to that PriorityQueue
.
Now you don't need to call sort, just iterate
over the queue
and display the result.
PriorityQueue<Task> queue = new PriorityQueue<>((o1,o2)-> o1.getDate.comapreTo(o2.getDate));
// Add task object to the queue
queue.add(myTaskObject);
//iterate and display when asked
Iterator<Task> it = queue.iterator();
while(it.hasNext()) {
System.out.println(it.next().toString());
}
Note: adding task object to queue is O(logn) So this will improve time of your solution
Comments
It would be better if you just maintain a List
of Task
instances and sort that List
.
You can use one of the following options to sort a List
of Task
instances:
- Implementing Comparable Interface
- Using Comparator
Implementing Comparable Interface
To implement Comparable
interface, you must override compareTo
method in Task
class. Since you want to sort Tasks based on date
instance field, you can just return the result of date comparison.
Here's how you should override compareTo()
method to sort Tasks in ascending order based on date
instance field.
@Override
public int compareTo(Task o) {
return this.date.compareTo(o.date);
}
Since Date
class already implements Comparable
interface, you can just call compareTo
method to compare two Date
instances.
Now to sort the list of tasks, call sort
method of Collections
class.
Collections.sort(taskList);
Here's a version of your code that implements Comparable
interface and sorts the tasks using date
instance field
Using Comparator
There are more than one ways to sort objects using a Comparator
interface:
- Create a separate class that implements
Comparator
interface - Use anonymous class or use lambda expression
- Use static methods of Comparator interface
Create a separate class that implements Comparator interface
You can create a class that implements Comparator
interface and then override compare
function. Implementation of compare
function will be same as that of compareTo
function implemented above by implementing Comparable
interface.
class TaskComparator implements Comparator<Task> {
@Override
public int compare(Task o1, Task o2) {
return o1.getDate().compareTo(o2.getDate());
}
}
To sort the task list, you have two options:
Use
sort
function ofCollections
class and pass an instacne ofTaskComparator
class as a second argumentCollections.sort(taskList, new TaskComparator());
Use
sort
method ofList
interfacetaskList.sort(new TaskComparator());
Here's a version of your code that creates a separate comparator class to sort the tasks using date
instance field
Use anonymous class or use lambda expression
Instead of creating a separate class to implement Comparator
interface, you can use an anonymous class
Collections.sort(taskList, new Comparator<Task>() {
@Override
public int compare(Task t1, Task t2) {
// code to compare Task objects
}
});
or
taskList.sort(new Comparator<Task>() {
@Override
public int compare(Task o1, Task o2) {
return o1.getDate().compareTo(o2.getDate());
}
});
Java 8 introduced lambda expressions, you can replace anonymous class with lambda expression to make your code concise
Collections.sort(taskList, (o1, o2) -> o1.getDate().compareTo(o2.getDate()));
or
taskList.sort((o1, o2) -> o1.getDate().compareTo(o2.getDate()));
Here's a version of your code that uses lambda expression to implement Comparator
interface
Use static methods of Comparator interface
You can also use static method named comparing
of Comparator
interface. It will return a comparator that will be used for sorting.
Collections.sort(taskList, Comparator.comparing(Task::getDate));
or
taskList.sort(Comparator.comparing(Task::getDate));
Here's a version of your code that uses Comparator.comparing
method to sort the tasks using date
instance field
For details on how to implement Comparable
or Comparator
interfaces, see:
9 Comments
Task
is not a comparatorComparator
in Task
would be really weird. If anything, it should be some separate class.List
of Task
instances and sorting that list using one of the options mentioned in the answer.