TreeModel is extremely inefficient when dealing with deep tree paths --------------------------------------------------------------------
Key: TRINIDAD-2013 URL: https://issues.apache.org/jira/browse/TRINIDAD-2013 Project: MyFaces Trinidad Issue Type: Improvement Affects Versions: 2.0.0-alpha-2, 1.2.12-core Reporter: Blake Sullivan Assignee: Blake Sullivan Priority: Minor Given the tree path to a node in a TreeModel, there is no mechanism for retrieving efficient tree path implementations for each of the ancestor nodes. This is because the only way of retrieving an ancestor node is to call TreeModel.getContainerRowKey(Object path) to retrieve each ancestor key and then pass the returned key in to retrieve its ancestor, etc. For efficiency, List-based keys (which are essentially all of the keys), return a subList(), trimming off the end element. As the initial lists get longer, the nest subLists become deeper as well. Even worse is when one these keys is used as a key into a Set or Map since the defined implementation of hashCode on a List is to hash each of the elements. As a result, the following test code called next() on the ListIterator 800 million times when passed a 374 element path: private static boolean _testN3Sublist(List<?> testList) { Set<?> expanded = new HashSet(); List<?> currKey = testList; do { boolean parentExpanded = expanded.contains(currKey); parentExpanded = expanded.contains(currKey); if (!parentExpanded) { int newSize = currKey.size() - 1; if (newSize == 0) return false; currKey = currKey.subList(0, newSize); } else { break; } } while (true); return true; } There are four parts to the solution: 1) Add /** * Returns the nth ancestor rowKey or <code>null</code> if no such ancestor exists. * This is more efficient than calling <code>getContainerRowKey(Object)</code> * on each rowKey in turn. * <p> * If generationFromChild is 0, the childRowKey will be returned. * <p> * While for backwards compatibility, <code>getAncestorRowKey</code> is implemented in terms * of <code>getContainerRowKey</code>, Path-based subclasses should implement * <code>getContainerRowKey</code> in terms of <code>getAncestorRowKey</code> to avoid * creating deeply nested sublists of keys. * @param childRowKey * @param generationFromChild * @return the nth ancestor rowKey or <code>null</code> if no such ancestor exists * @throws NullPointerException if childRowKey is <code>null</code> * @throws IllegalArgumentException if generationFromChild is less than 0 * @see #getContainerRowKey */ public Object getAncestorRowKey(Object childRowKey, int generationFromChild) { if (generationFromChild < 0) throw new IllegalArgumentException(); while (generationFromChild >= 0) { childRowKey = getContainerRowKey(childRowKey); generationFromChild--; if (childRowKey == null) break; } return childRowKey; } 2) Change List-based TreeModel subclasses to override public Object getAncestorRowKey(Object childRowKey, int generationFromChild) With an implementation providing exactly the needed subList 3) Change code that iterates up the keys to pass in the generation of the List that they want. This avoids any nesting of subLists 4) In many cases, the resulting Lists are immutable, so add the following api to allow TreeModel implementations to speed up the hashCode calculation as well: /** * Returns a new immutable wrapper list based on an existing list <code>l</code>. * <p> * Once an immutable wrapper has been created on <code>l</code>, neither * <code>l</code> nor the contents of List <code>l</code> may be changed. * <p> * In return for this restriction, the immutable List offers much faster * implementations of operations such as <code>hashCode</code> and * <code>subList</code>. This makes the instances returned for use as keys into * Sets or Maps, especially since the immutability restrictions are identical. * @param l The List to create an immutable wrapper on * @return A new immutable list on <code>l</code> * @throws NullPointerException if <code>l</code> is null. */ public static <T> List<? extends T> newImmutableList(List<? extends T> l) -- This message is automatically generated by JIRA. - You can reply to this email to add a comment to the issue online.