How to listen the Change of the HashMap's specific key's Value in Java

Viewed 159

I have to wait till the HashMap key's value change from another thread and have to continue the request processing after that.

1 Answers

I think the most flexible solution will be to implement the Observer pattern.


enter image description here


Note that build-inn interfaces Observer and Observable are deprecated since JDK version 9. Even if you are using Java 8 don't use them. The problem with them is that their names as well as the method name update() of the Observer don't tell anything to the reader of the code about the event that was triggered and what kind of actions might follow.

Ass you can see from the diagram, the subject should contain a collection of observers (or listeners).

The subject in your case should be class contains a HashMap updates of which you want to listen. Don't extend the map, wrap it with your class instead. Because if you choose to extend HashMap your code will become dependent on its implementation. And any changes in the HashMap, for instance, new methods were added or existing were improved could break your code (this topic is covered in the book "Effective Java" by Joshua Blochtake, have a look at it for more information).

So let's say we have a class OrderManager that maintains a map of Orders. This class will be a subject.

A couple of services like BillingManager, LoggingManagerand maybe some more needs to notified new order was added. These are our ***observers***. All these classes an interface, let's sayOrderAddedListenerthat defines a single methodonOrderAdded(Order order)`, that's the event we are interested in.

Note, if you need to listen to other events like removal or update, you need to define a new interface with a method responsible for that for every target event as the Interface segregation principle suggests.

OrderManager has to have a collection of observers. When a new order is being added, subject iterates through the collection of observers and invokes onOrderAdded() method on each of them.

In order to add an observer that need to listen to the order-added event OrderManager has to define a method to register it, and it's also good practice to add another one to unregister the observer that has registered to be able to remove it when you no longer need it.

Asynchronous processing

Note, that in this example, events are being processing in the same thread. If actions performed by observers are costful or might block the thread, in order to fire them asynchronously you can create a class that will implement Runnable and hold references to the observer and event (order the was added/updated), and method run() will trigger the observer. And when a new event occurs, OrderManager instead of invoking onOrderAdded() on each observer should create a new instance of that class implementing runnable by passing an observer and a new order to its constructor and then create and fire a new thread.

It's a simplified approach. But I guess it'll give an understanding of the general idea.

Implementation example

That how it might look like:

public class OrderManager {
    private Map<Long, Order> orderById = new HashMap<>();
    private Set<OrderAddedListener> listeners = new HashSet<>();
    
    public void addOrder(Order order) {
        // notifying observers
        listeners.forEach(observer -> observer.onOrderAdded(order));
        
        orderById.put(order.getId(), order);
    }
    
    // more methods like removeOrder(), getOrderCount() etc.
    
    public boolean registerOrderAddedListener(OrderAddedListener listener) {
        return listeners.add(listener);
    }
    
    public boolean unregisterOrderAddedListener(OrderAddedListener listener) {
        return listeners.remove(listener);
    }
}

public interface OrderAddedListener {
    void onOrderAdded(Order order);
}

public class LoggingManager implements OrderAddedListener {
    private Logger logger;
    
    @Override
    public void onOrderAdded(Order order) {
//        logger.log();
        System.out.println("Log message has been written");
    }
}

public class BillingManager implements OrderAddedListener {
    private BillingService billingService;
    
    @Override
    public void onOrderAdded(Order order) {
//        billingService.sendBill(order);
        System.out.println("A bill has been sent");
    }
}

main() - a simple demo

public static void main(String[] args) {
    OrderManager orderManager = new OrderManager();
    orderManager.registerOrderAddedListener(new LoggingManager());
    orderManager.registerOrderAddedListener(new BillingManager());
    orderManager.addOrder(new Order());
}

Output

A log message has been written
A bill has been sent
Related