diff --git a/core/src/main/java/dev/kske/eventbus/core/EventBus.java b/core/src/main/java/dev/kske/eventbus/core/EventBus.java index 334dd52..9f66c0c 100644 --- a/core/src/main/java/dev/kske/eventbus/core/EventBus.java +++ b/core/src/main/java/dev/kske/eventbus/core/EventBus.java @@ -90,6 +90,16 @@ public final class EventBus { */ private final Map, TreeSet> bindings = new ConcurrentHashMap<>(); + /** + * A cache mapping an event class to all handlers the event should be dispatched to. This + * includes polymorphic handlers that don't reference the event class explicitly. If an event + * class is not contained inside this cache, the {@link #bindings} have to be traversed manually + * in search of applicable handlers. + * + * @since 1.3.0 + */ + private final Map, TreeSet> bindingCache = new ConcurrentHashMap<>(); + /** * Stores all registered event listeners (which declare event handlers) and prevents them from * being garbage collected. @@ -175,24 +185,32 @@ public final class EventBus { * Searches for the event handlers bound to an event class. This includes polymorphic handlers * that are bound to a supertype of the event class. * + * @implNote If the given event type was requested in the past, the handlers are retrieved from + * the {@link #bindingCache}. If not, the entire {@link #bindings} are traversed in + * search of polymorphic handlers compatible with the event type. * @param eventType the event type to use for the search * @return a navigable set containing the applicable handlers in descending order of priority * @since 1.2.0 */ private NavigableSet getHandlersFor(Class eventType) { + if (bindingCache.containsKey(eventType)) { + return bindingCache.get(eventType); + } else { - // Get handlers defined for the event class - TreeSet handlers = - bindings.getOrDefault(eventType, new TreeSet<>(byPriority)); + // Get handlers defined for the event class + TreeSet handlers = + bindings.getOrDefault(eventType, new TreeSet<>(byPriority)); - // Get polymorphic handlers - for (var binding : bindings.entrySet()) - if (binding.getKey().isAssignableFrom(eventType)) - for (var handler : binding.getValue()) - if (handler.isPolymorphic()) - handlers.add(handler); + // Get polymorphic handlers + for (var binding : bindings.entrySet()) + if (binding.getKey().isAssignableFrom(eventType)) + for (var handler : binding.getValue()) + if (handler.isPolymorphic()) + handlers.add(handler); - return handlers; + bindingCache.put(eventType, handlers); + return handlers; + } } /** @@ -369,15 +387,28 @@ public final class EventBus { } /** - * Inserts a new handler into the {@link #bindings} map. + * Inserts a new handler into the {@link #bindings} map. Additionally, the handler is placed + * inside the {@link #bindingCache} where applicable. * * @param handler the handler to bind * @since 1.2.0 */ private void bindHandler(EventHandler handler) { + + // Bind handler bindings.putIfAbsent(handler.getEventType(), new TreeSet<>(byPriority)); logger.log(Level.DEBUG, "Binding event handler {0}", handler); bindings.get(handler.getEventType()).add(handler); + + // Insert handler into cache + bindingCache.putIfAbsent(handler.getEventType(), new TreeSet<>(byPriority)); + bindingCache.get(handler.getEventType()).add(handler); + + // Handler is polymorphic => insert where applicable + if (handler.isPolymorphic()) + for (var binding : bindingCache.entrySet()) + if (binding.getKey().isAssignableFrom(handler.getEventType())) + binding.getValue().add(handler); } /** @@ -402,6 +433,18 @@ public final class EventBus { } } + // Remove bindings from cache + for (var binding : bindingCache.values()) { + var it = binding.iterator(); + while (it.hasNext()) { + var handler = it.next(); + if (handler.getListener() == listener) { + logger.log(Level.TRACE, "Removing event handler {0} from cache", handler); + it.remove(); + } + } + } + // Remove the listener itself registeredListeners.remove(listener); }