Merge changes Ia7cd7eda,If961a029,I9f6a57a2,Id8551489
[controller.git] / opendaylight / md-sal / sal-inmemory-datastore / src / main / java / org / opendaylight / controller / md / sal / dom / store / impl / ResolveDataChangeState.java
index 858087e2df02f6c511289472eb43548a5ca099ae..3db4115af67908bed3c4edfcfbb2d91cdae3baae 100644 (file)
@@ -118,15 +118,34 @@ final class ResolveDataChangeState {
      */
     public ResolveDataChangeState child(final PathArgument childId) {
         /*
-         * We instantiate a concatenation only when needed, otherwise
-         * we reuse the collection. This speeds up Iterables.isEmpty()
-         * in needsProcessing().
+         * We instantiate a concatenation only when needed:
+         *
+         * 1) If our collection is empty, we reuse the parent's. This is typically the case
+         *    for intermediate node, which should be the vast majority.
+         * 2) If the parent's iterable is a Collection and it is empty, reuse our collection.
+         *    This is the case for the first node which defines a subtree listener in a
+         *    particular subtree.
+         * 3) Concatenate the two collections. This happens when we already have some
+         *    subtree listeners and we encounter a node which adds a few more.
+         *
+         * This allows us to lower number of objects allocated and also
+         * speeds up Iterables.isEmpty() in needsProcessing().
+         *
+         * Note that the check for Collection in 2) relies on precisely this logic, which
+         * ensures that we simply cannot see an empty concatenation, but rather start off with
+         * an empty collection, then switch to a non-empty collection and finally switch to
+         * a concatenation. This saves us from instantiating iterators, which a trivial
+         * Iterables.isEmpty() would do as soon as we cross case 3).
          */
         final Iterable<Builder> sb;
-        if (subBuilders.isEmpty()) {
-            sb = inheritedSub;
+        if (!subBuilders.isEmpty()) {
+            if (inheritedSub instanceof Collection && ((Collection<?>) inheritedSub).isEmpty()) {
+                sb = subBuilders.values();
+            } else {
+                sb = Iterables.concat(inheritedSub, subBuilders.values());
+            }
         } else {
-            sb = Iterables.concat(inheritedSub, subBuilders.values());
+            sb = inheritedSub;
         }
 
         return new ResolveDataChangeState(nodeId.node(childId), sb,
@@ -156,12 +175,25 @@ final class ResolveDataChangeState {
         if (!inheritedOne.isEmpty()) {
             return true;
         }
-        // Have SUBTREE listeners
-        if (!Iterables.isEmpty(inheritedSub)) {
-            return true;
+
+        /*
+         * Have SUBTREE listeners
+         *
+         * This is slightly magical replacement for !Iterables.isEmpty(inheritedSub).
+         * It relies on the logic in child(), which gives us the guarantee that when
+         * inheritedSub is not a Collection, it is guaranteed to be non-empty (which
+         * means we need to process). If it is a collection, we still need to check
+         * it for emptiness.
+         *
+         * Unlike Iterables.isEmpty() this code does not instantiate any temporary
+         * objects and is thus more efficient.
+         */
+        if (inheritedSub instanceof Collection) {
+            return !((Collection<?>) inheritedSub).isEmpty();
         }
 
-        return false;
+        // Non-Collection => non-empty => have to process
+        return true;
     }
 
     /**