From 0833b78044185ddf239b4680ad3173a841b32067 Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Fri, 24 May 2019 12:27:32 -0700 Subject: [PATCH] Add a secondary index to VoltXMLElement for children findChild() was executing a linear search. The secondary index allows a hash lookup, which is faster. --- .../hsqldb_voltpatches/VoltXMLElement.java | 64 +++++++++++++++++-- 1 file changed, 57 insertions(+), 7 deletions(-) diff --git a/src/hsqldb19b3/org/hsqldb_voltpatches/VoltXMLElement.java b/src/hsqldb19b3/org/hsqldb_voltpatches/VoltXMLElement.java index d7a1c329588..bd305d36969 100644 --- a/src/hsqldb19b3/org/hsqldb_voltpatches/VoltXMLElement.java +++ b/src/hsqldb19b3/org/hsqldb_voltpatches/VoltXMLElement.java @@ -18,6 +18,8 @@ package org.hsqldb_voltpatches; import com.google_voltpatches.common.base.Strings; +import com.google_voltpatches.common.collect.LinkedListMultimap; +import com.google_voltpatches.common.collect.ListMultimap; import com.google_voltpatches.common.hash.Hasher; import com.google_voltpatches.common.hash.Hashing; @@ -33,7 +35,14 @@ public class VoltXMLElement { public String name; public final Map attributes = new TreeMap<>(); - public final List children = new ArrayList<>(); + + // This used to be a plain list, but we wanted a secondary index on + // the elements and the VoltDB team doesn't believe in encapsulation + // (this list was made public for some reason and now has 250+ references + // to it throughout the codebase). So we've introduced this gross hack, + // which enables us to act like a List with the added benefit of a + // secondary index. + public final SecondaryIndexList children = new SecondaryIndexList(); public VoltXMLElement(String name) { this.name = name; @@ -202,12 +211,7 @@ public List findChildren(String name) */ public VoltXMLElement findChild(String uniqueName) { - for (VoltXMLElement vxe : children) { - if (uniqueName.equals(vxe.getUniqueName())) { - return vxe; - } - } - return null; + return children.getByUniqueName(uniqueName); } /** @@ -521,4 +525,50 @@ public Integer getIntAttribute(String key, Integer defval) { } return(Integer.parseInt(valstr)); } + + public class SecondaryIndexList extends AbstractList { + private final List childList = new ArrayList<>(); + private final ListMultimap childUniqueNameIndex = LinkedListMultimap.create(); + + SecondaryIndexList() { + } + + VoltXMLElement getByUniqueName(String uniqueName) { + final List elements = childUniqueNameIndex.get(uniqueName.toUpperCase()); + if (elements.isEmpty()) { + return null; + } + return elements.get(0); + } + + @Override + public VoltXMLElement get(int index) { + return childList.get(index); + } + + @Override + public int size() { + return childList.size(); + } + + @Override + public VoltXMLElement set(int index, VoltXMLElement element) { + final VoltXMLElement setElement = childList.set(index, element); + childUniqueNameIndex.put(element.getUniqueName().toUpperCase(), element); + return setElement; + } + + @Override + public void add(int index, VoltXMLElement element) { + childList.add(index, element); + childUniqueNameIndex.put(element.getUniqueName().toUpperCase(), element); + } + + @Override + public VoltXMLElement remove(int index) { + final VoltXMLElement previous = childList.remove(index); + assert(childUniqueNameIndex.remove(previous.getUniqueName().toUpperCase(), previous)); + return previous; + } + } }