Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Queue Practice | Additional Data Structures
Java Data Structures
course content

Course Content

Java Data Structures

Java Data Structures

1. Basic Data Structures
2. Additional Data Structures
3. Map
4. enum & Stream API

book
Queue Practice

Practice

It's time to practice. You are given a task to write a TaskManager service that will keep track of your tasks. With this service, the user can add tasks, assign them to themselves, and then complete them in a sequential order. Let's start working on this task together, and then you'll continue on your own.

Note

In this assignment, we will use the interface-implementation structure. First, we'll write an interface that will define the methods to be implemented by the inheriting classes.

But first, we need to create the task model itself. Let's create a Task class:

java

main

copy
12345
public class Task { private int id; private String name; private String description; }

The Task class contains 3 attributes: id, name, and description. Each task must have a name and description, and id is a necessary attribute for any data structure. How else would we access the elements? The attributes are also protected by the private access modifier because we don't want other classes to have direct access to the fields of this class, except through the constructor or getters.

Now, let's implement these getters and the constructor. This can be easily done using the combination of key control + return on a MacBook.

After the performed operations, we have the following Task class:

java

Task

copy
12345678910111213141516171819202122232425262728293031323334
package codefinity.taskManager; public class Task { private int id; private String name; private String description; public Task(int id, String name, String description) { this.id = id; this.name = name; this.description = description; } public int getId() { return id; } public String getName() { return name; } public String getDescription() { return description; } @Override public String toString() { return "Task{" + "id=" + id + ", name='" + name + '\'' + ", description='" + description + '\'' + '}'; } }

Services

We will have a very simplified task board service, so let's implement a service that will add, mark as completed, and check if there are tasks in the queue. Let's call this class TaskQueueService. To begin with, we need to define an interface with these tasks:

TaskQueueService

TaskQueueService

copy
123456789
package codefinity.taskManager; public interface TaskQueueService { void addTask(Task task); Task getNextTask(); boolean isEmpty(); }

This interface defines 3 methods that implementation classes must implement. These methods include adding a task, moving to the next task (implying that the user has already completed the previous task), and a method to check if there are tasks in the queue.

Great, let's now create an implementation class that will override and implement all these methods:

TaskQueueServiceImpl

TaskQueueServiceImpl

copy
12345678910
package codefinity.taskManager.taskManagerImpl; import codefinity.taskManager.Task; import java.util.LinkedList; import java.util.Queue; public class TaskQueueServiceImpl { private Queue<Task> taskQueue = new LinkedList<>(); }

We create a private attribute that will be used only in this service class.

It's worth making a small digression and talking about service classes, which are a separate part of OOP. Service classes are created to perform specific operations. They inherit from service interfaces and implement their methods. This approach to writing an application helps maintain and extend it while adhering to the SOLID principles, which we will study in a separate course. At the moment, it's important to understand that such classes are created solely to perform operations on other objects.

For example: If we write a calculator, we will have a separate class that will store the value of the number we operate on. The actual operations will be performed using a service class where addition, subtraction, and so on are defined.

I hope this clarifies the purpose of service classes in solving the task at hand.

Service Implementation

Let's continue the task execution. Our next task is to show that this class implements the TaskQueueService interface. To do this, we use the keyword implements and specify the interface that the class implements. We also need to override the methods defined by the interface. IntelliJ IDEA also allows us to significantly simplify this task using the combination option + return.

Now, in our class, all the methods that need to be overridden are shown. Let's go ahead and do it!

java

TaskQueueServiceImpl

copy
1234567891011121314151617181920212223242526
package codefinity.taskManager.taskManagerImpl; import codefinity.taskManager.Task; import codefinity.taskManager.TaskQueueService; import java.util.LinkedList; import java.util.Queue; public class TaskQueueServiceImpl implements TaskQueueService { private Queue<Task> taskQueue = new LinkedList<>(); @Override public void addTask(Task task) { taskQueue.offer(task); } @Override public Task getNextTask() { return taskQueue.poll(); } @Override public boolean isEmpty() { return taskQueue.isEmpty(); } }

In our case, nothing overly complicated was done. We simply used the methods of the Queue class to implement our methods correctly. Thus, a data structure like a queue makes life easier for all Java programmers.

We use the offer() method for adding, the poll() method for removal, and the isEmpty() method to check if the queue is empty.

Now it's your turn to play a role in solving this problem. You'll need to implement a service class, the interface of which we will create together:

java

main

copy
12345
package codefinity.taskManager; public interface TaskProcessorService { void processTasks(); }

We've created a service interface with one method: processTasks(), which, when invoked, should start executing all tasks until the task list is empty. This method represents a user starting to perform tasks.

Note

If you want to make the task a bit more challenging, you can add a method for processing a single task. In that case, you need to create a separate method, processTask(), in this interface. This method should process the first task in the list.

Task

Your task is to write a class TaskProcessorServiceImpl that should implement the TaskProcessorService interface. This class should have a method to process all tasks, meaning it should use methods from the TaskQueueService service. You can use composition by creating an instance of this class within the class you need to implement, for example:

java

main

copy
1234567
package codefinity.taskManager.taskManagerImpl; import codefinity.taskManager.TaskQueueService; public class TaskProcessorServiceImpl { TaskQueueService taskQueueService = new TaskQueueServiceImpl(); }

In general, there is already a prepared composition at the top for your task. Next, you just need to implement one method using an instance of this class.

After that, you need to create a constructor that will initialize this object of the taskQueueService class.

This way, you'll be able to use the methods of this object. It's also evident that, for task processing, you need to pass the queue with which the taskProcessor will work.

The rest of the task is left to you. Hints will be provided in the README.md file. Once your solution is ready, click on the "Run Tests" button, and the unit tests I have written will check your solution.

Everything was clear?

How can we improve it?

Thanks for your feedback!

Section 2. Chapter 3
We're sorry to hear that something went wrong. What happened?
some-alt