简体   繁体   中英

How to sort elements of ArrayList in ascending order? - Java

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);
    }
}

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() ).

First 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

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:

  1. Implementing Comparable Interface
  2. 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:

  1. Use sort function of Collections class and pass an instacne of TaskComparator class as a second argument

     Collections.sort(taskList, new TaskComparator());
  2. Use sort method of List interface

    taskList.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:

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(...));

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM