🧠
Improved Approach with HashSet and Event Filtering
💡 Using a hash set for observers improves removal efficiency. Adding event filtering allows notifying only interested observers, making the system more scalable and flexible.
Intuition
Store observers in a hash set for O(1) add/remove. Each observer registers interest in specific event types. Notify only observers interested in the event type.
Algorithm
- Use a hash set or dictionary to store observers for O(1) add/remove.
- Allow observers to specify event types they want to receive.
- Maintain a mapping from event types to sets of observers.
- When notifying, only iterate over observers subscribed to that event type.
💡 This adds complexity but improves efficiency and flexibility by avoiding unnecessary notifications.
class Observer:
def __init__(self):
self.events = set()
def update(self, event_type, message):
pass
class Subject:
def __init__(self):
self.observers = {}
def subscribe(self, observer, event_type):
if event_type not in self.observers:
self.observers[event_type] = set()
self.observers[event_type].add(observer)
def unsubscribe(self, observer, event_type):
if event_type in self.observers and observer in self.observers[event_type]:
self.observers[event_type].remove(observer)
if not self.observers[event_type]:
del self.observers[event_type]
def notify(self, event_type, message):
if event_type in self.observers:
for observer in self.observers[event_type]:
observer.update(event_type, message)
class ConcreteObserver(Observer):
def __init__(self, name):
super().__init__()
self.name = name
def update(self, event_type, message):
print(f"{self.name} received {event_type}: {message}")
# Example usage
subject = Subject()
obs1 = ConcreteObserver("Observer A")
obs2 = ConcreteObserver("Observer B")
subject.subscribe(obs1, "news")
subject.subscribe(obs2, "sports")
subject.notify("news", "News event 1")
subject.notify("sports", "Sports event 1")
subject.unsubscribe(obs1, "news")
subject.notify("news", "News event 2")
Line Notes
self.observers = {}Dictionary maps event types to sets of observers for quick lookup and efficient filtering
self.observers[event_type].add(observer)Add observer to the set for the specific event type to receive relevant notifications
if event_type in self.observers:Notify only observers subscribed to this event type to avoid unnecessary calls
for observer in self.observers[event_type]:Iterate over relevant observers to notify them about the event
import java.util.*;
interface Observer {
void update(String eventType, String message);
}
class Subject {
private Map<String, Set<Observer>> observers = new HashMap<>();
public void subscribe(Observer observer, String eventType) {
observers.computeIfAbsent(eventType, k -> new HashSet<>()).add(observer);
}
public void unsubscribe(Observer observer, String eventType) {
if (observers.containsKey(eventType)) {
Set<Observer> set = observers.get(eventType);
set.remove(observer);
if (set.isEmpty()) {
observers.remove(eventType);
}
}
}
public void notifyObservers(String eventType, String message) {
if (observers.containsKey(eventType)) {
for (Observer observer : observers.get(eventType)) {
observer.update(eventType, message);
}
}
}
}
class ConcreteObserver implements Observer {
private String name;
public ConcreteObserver(String name) {
this.name = name;
}
public void update(String eventType, String message) {
System.out.println(name + " received " + eventType + ": " + message);
}
}
public class Main {
public static void main(String[] args) {
Subject subject = new Subject();
ConcreteObserver obs1 = new ConcreteObserver("Observer A");
ConcreteObserver obs2 = new ConcreteObserver("Observer B");
subject.subscribe(obs1, "news");
subject.subscribe(obs2, "sports");
subject.notifyObservers("news", "News event 1");
subject.notifyObservers("sports", "Sports event 1");
subject.unsubscribe(obs1, "news");
subject.notifyObservers("news", "News event 2");
}
}
Line Notes
private Map<String, Set<Observer>> observers = new HashMap<>();Map event types to sets of observers for efficient event filtering and quick access
observers.computeIfAbsent(eventType, k -> new HashSet<>()).add(observer);Add observer to the set for the event type, creating set if missing to maintain subscriptions
if (observers.containsKey(eventType))Check if any observers subscribed to this event type before notifying to avoid null errors
for (Observer observer : observers.get(eventType))Notify only observers interested in this event type to improve efficiency
#include <iostream>
#include <unordered_map>
#include <unordered_set>
#include <string>
class Observer {
public:
virtual void update(const std::string& eventType, const std::string& message) = 0;
virtual ~Observer() {}
};
class Subject {
private:
std::unordered_map<std::string, std::unordered_set<Observer*>> observers;
public:
void subscribe(Observer* observer, const std::string& eventType) {
observers[eventType].insert(observer);
}
void unsubscribe(Observer* observer, const std::string& eventType) {
if (observers.count(eventType)) {
observers[eventType].erase(observer);
if (observers[eventType].empty()) {
observers.erase(eventType);
}
}
}
void notify(const std::string& eventType, const std::string& message) {
if (observers.count(eventType)) {
for (auto observer : observers[eventType]) {
observer->update(eventType, message);
}
}
}
};
class ConcreteObserver : public Observer {
private:
std::string name;
public:
ConcreteObserver(const std::string& n) : name(n) {}
void update(const std::string& eventType, const std::string& message) override {
std::cout << name << " received " << eventType << ": " << message << std::endl;
}
};
int main() {
Subject subject;
ConcreteObserver obs1("Observer A");
ConcreteObserver obs2("Observer B");
subject.subscribe(&obs1, "news");
subject.subscribe(&obs2, "sports");
subject.notify("news", "News event 1");
subject.notify("sports", "Sports event 1");
subject.unsubscribe(&obs1, "news");
subject.notify("news", "News event 2");
return 0;
}
Line Notes
std::unordered_map<std::string, std::unordered_set<Observer*>> observers;Map event types to sets of observer pointers for fast lookup and filtering
observers[eventType].insert(observer);Insert observer pointer into the set for the event type to subscribe
if (observers.count(eventType))Check if observers exist for the event type before notifying to avoid errors
for (auto observer : observers[eventType])Notify only observers subscribed to this event type to improve efficiency
class Observer {
update(eventType, message) {}
}
class Subject {
constructor() {
this.observers = new Map();
}
subscribe(observer, eventType) {
if (!this.observers.has(eventType)) {
this.observers.set(eventType, new Set());
}
this.observers.get(eventType).add(observer);
}
unsubscribe(observer, eventType) {
if (this.observers.has(eventType)) {
this.observers.get(eventType).delete(observer);
if (this.observers.get(eventType).size === 0) {
this.observers.delete(eventType);
}
}
}
notify(eventType, message) {
if (this.observers.has(eventType)) {
this.observers.get(eventType).forEach(observer => observer.update(eventType, message));
}
}
}
class ConcreteObserver extends Observer {
constructor(name) {
super();
this.name = name;
}
update(eventType, message) {
console.log(`${this.name} received ${eventType}: ${message}`);
}
}
// Example usage
const subject = new Subject();
const obs1 = new ConcreteObserver("Observer A");
const obs2 = new ConcreteObserver("Observer B");
subject.subscribe(obs1, "news");
subject.subscribe(obs2, "sports");
subject.notify("news", "News event 1");
subject.notify("sports", "Sports event 1");
subject.unsubscribe(obs1, "news");
subject.notify("news", "News event 2");
Line Notes
this.observers = new Map();Use a Map to associate event types with sets of observers for efficient filtering
this.observers.get(eventType).add(observer);Add observer to the set for the event type to subscribe
if (this.observers.has(eventType))Check if observers exist for event type before notifying to avoid errors
this.observers.get(eventType).forEach(observer => observer.update(...))Notify only observers subscribed to the event type to improve efficiency
TimeO(k) per notification where k is number of observers subscribed to event type
SpaceO(n) for storing all observers across event types
Using sets and maps reduces notification to only relevant observers, improving efficiency over brute force. Add and remove operations are O(1) average due to hash sets.
💡 If 1000 observers exist but only 10 subscribe to 'news', notifying 'news' only calls 10 updates, saving time.
Interview Verdict: Accepted - better scalability and flexibility than brute force
This approach is more practical for real-world event systems where observers subscribe to specific events, improving performance and maintainability.