You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by lu...@apache.org on 2012/03/06 17:58:54 UTC

svn commit: r1297574 - in /myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component: UIComponent.java UIComponentBase.java UIInput.java _ArrayMap.java _ComponentFacetMap.java behavior/BehaviorBase.java

Author: lu4242
Date: Tue Mar  6 16:58:54 2012
New Revision: 1297574

URL: http://svn.apache.org/viewvc?rev=1297574&view=rev
Log:
MYFACES-3435 [perf] _DeltaList: use ArrayList as parent (adjust size of structures to match typical values and replace HashMap with alternative ArrayMap).

Added:
    myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/_ArrayMap.java   (with props)
Modified:
    myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/UIComponent.java
    myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/UIComponentBase.java
    myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/UIInput.java
    myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/_ComponentFacetMap.java
    myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/behavior/BehaviorBase.java

Modified: myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/UIComponent.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/UIComponent.java?rev=1297574&r1=1297573&r2=1297574&view=diff
==============================================================================
--- myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/UIComponent.java (original)
+++ myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/UIComponent.java Tue Mar  6 16:58:54 2012
@@ -789,7 +789,9 @@ public abstract class UIComponent implem
         // Make sure the list for class exists
         if (listeners == null)
         {
-            listeners = new _DeltaList<SystemEventListener>(new ArrayList<SystemEventListener>(2));
+            // how many listeners per event type can single component have? 
+            // We use 3 here as expected number, but it is a question 
+            listeners = new _DeltaList<SystemEventListener>(new ArrayList<SystemEventListener>(3));
             _systemEventListenerClassMap.put(eventClass, listeners);
         }
 

Modified: myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/UIComponentBase.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/UIComponentBase.java?rev=1297574&r1=1297573&r2=1297574&view=diff
==============================================================================
--- myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/UIComponentBase.java (original)
+++ myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/UIComponentBase.java Tue Mar  6 16:58:54 2012
@@ -359,7 +359,9 @@ public abstract class UIComponentBase ex
             List<ClientBehavior> behaviorsForEvent = _behaviorsMap.get(eventName);
             if(behaviorsForEvent == null)
             {
-                behaviorsForEvent = new _DeltaList<ClientBehavior>(new ArrayList<ClientBehavior>());
+                // Normally have client only 1 client behaviour per event name,
+                // so size 2 must be sufficient: 
+                behaviorsForEvent = new _DeltaList<ClientBehavior>(new ArrayList<ClientBehavior>(2));
                 _behaviorsMap.put(eventName, behaviorsForEvent);
             }
             
@@ -1143,7 +1145,8 @@ public abstract class UIComponentBase ex
         }
         if (_facesListeners == null)
         {
-            _facesListeners = new _DeltaList<FacesListener>(new ArrayList<FacesListener>());
+            // How many facesListeners have single component normally? 
+            _facesListeners = new _DeltaList<FacesListener>(new ArrayList<FacesListener>(5));
         }
         _facesListeners.add(listener);
     }

Modified: myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/UIInput.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/UIInput.java?rev=1297574&r1=1297573&r2=1297574&view=diff
==============================================================================
--- myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/UIInput.java (original)
+++ myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/UIInput.java Tue Mar  6 16:58:54 2012
@@ -786,7 +786,8 @@ public class UIInput extends UIOutput im
         
         if (_validatorList == null)
         {
-            _validatorList = new _DeltaList<Validator>(new ArrayList<Validator>());
+            //normally add user 0-3 validators: 
+            _validatorList = new _DeltaList<Validator>(new ArrayList<Validator>(3));
         }
 
         _validatorList.add(validator);

Added: myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/_ArrayMap.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/_ArrayMap.java?rev=1297574&view=auto
==============================================================================
--- myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/_ArrayMap.java (added)
+++ myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/_ArrayMap.java Tue Mar  6 16:58:54 2012
@@ -0,0 +1,637 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package javax.faces.component;
+
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+/**
+ * A Map implementation that stores its contents in a single
+ * array.  This approach is significantly faster for small sets of
+ * data than the use of a HashMap or Hashtable, though potentially
+ * much slower for very large sets.
+ * <p>
+ * ArrayMap is optimized for many-reads-few-write.  In particular,
+ * it reallocates its array on any insertion or deletion.
+ * <p>
+ * ArrayMap also includes a series of static methods for managing the
+ * Object array. These may be used in place of instantiating an
+ * ArrayMap for clients that don't need a Map implementation.
+ * Clients using these methods must be careful to store the returned
+ * Object array on any mutator method.  They also must provide their
+ * own synchronization, if needed.  When using these static methods,
+ * clients can opt to search for objects by identity (via
+ * <code>getByIdentity()</code>) instead of equality, while the static
+ * <code>get()</code> method will try identity before equality.  This
+ * latter approach is extremely fast but still safe for retrieval of
+ * Strings that have all been interned, especially if misses are
+ * infrequent (since misses do require a scan using Object.equals()).
+ * It's worth remembering that String constants are always interned,
+ * as required by the language specification.
+ * <p>
+ * @version $Name:  $ ($Revision: adfrt/faces/adf-faces-api/src/main/java/oracle/adf/view/faces/util/ArrayMap.java#0 $) $Date: 10-nov-2005.19:08:36 $
+ */
+// -= Simon Lessard =-
+//        Using a single array for both the key and the value leads to many 
+//        problems, especially with type safety. Using parallel arrays or 
+//        a single array containing nodes would be a much cleaner/safer idea.
+// =-= AdamWiner =-=
+//        True, but the whole point of this class is maximal efficiency for
+//        small, transient arrays.  The type safety problems are entirely internal,
+//        not exposed to clients.  Parallel arrays or arrays containing nodes
+//        would be less efficient - if you're willing to allocate bonus objects,
+//        and don't care about efficiency, just use HashMap.
+class _ArrayMap<K, V> extends AbstractMap<K, V> implements Cloneable
+{
+    /**
+     * Creates an empty ArrayMap, preallocating nothing.
+     */
+    public _ArrayMap()
+    {
+        this(0, 1);
+    }
+
+    /**
+     * Creates an ArrayMap, preallocating for a certain size.
+     * @param size the number of elements to pre-allocate for
+     */
+    public _ArrayMap(int size)
+    {
+        this(size, 1);
+    }
+
+    /**
+     * Creates an ArrayMap, preallocating for a certain size.
+     * @param size the number of elements to pre-allocate for
+     * @param increment the number of additional elements to
+     *     allocate for when overruning
+     */
+    public _ArrayMap(int size, int increment)
+    {
+        if ((increment < 1) || (size < 0))
+        {
+            throw new IllegalArgumentException();
+        }
+
+        if (size > 0)
+        {
+            _array = new Object[2 * size];
+        }
+
+        _increment = increment;
+    }
+
+    /**
+     * Returns the key at a specific index in the map.
+     */
+    @SuppressWarnings("unchecked")
+    public K getKey(int index)
+    {
+        if ((index < 0) || (index >= size()))
+        {
+            throw new IndexOutOfBoundsException();
+        }
+        return (K) _array[index * 2];
+    }
+
+    /**
+     * Returns the value at a specific index in the map.
+     */
+    @SuppressWarnings("unchecked")
+    public V getValue(int index)
+    {
+        if ((index < 0) || (index >= size()))
+        {
+            throw new IndexOutOfBoundsException();
+        }
+        return (V) _array[index * 2 + 1];
+    }
+
+    /**
+     * Gets the object stored with the given key.  Scans first
+     * by object identity, then by object equality.
+     */
+    static public Object get(Object[] array, Object key)
+    {
+        Object o = getByIdentity(array, key);
+        if (o != null)
+        {
+            return o;
+        }
+
+        return getByEquality(array, key);
+    }
+
+    /**
+     * Gets the object stored with the given key, using
+     * only object identity.
+     */
+    static public Object getByIdentity(Object[] array, Object key)
+    {
+        if (array != null)
+        {
+            int length = array.length;
+            for (int i = 0; i < length; i += 2)
+            {
+                if (array[i] == key)
+                {
+                    return array[i + 1];
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Gets the object stored with the given key, using
+     * only object equality.
+     */
+    static public Object getByEquality(Object[] array, Object key)
+    {
+        if (array != null)
+        {
+            int length = array.length;
+
+            for (int i = 0; i < length; i += 2)
+            {
+                Object targetKey = array[i];
+                if (targetKey == null)
+                {
+                    return null;
+                }
+                else if (targetKey.equals(key))
+                {
+                    return array[i + 1];
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Adds the key/value pair to the array, returning a
+     * new array if necessary.
+     */
+    static public Object[] put(Object[] array, Object key, Object value)
+    {
+        if (array != null)
+        {
+            int length = array.length;
+
+            for (int i = 0; i < length; i += 2)
+            {
+                Object curKey = array[i];
+
+                if (((curKey != null) && (curKey.equals(key)))
+                        || (curKey == key))
+                {
+                    array[i + 1] = value;
+                    return array;
+                }
+            }
+        }
+
+        return _addToArray(array, key, value, 1);
+    }
+
+    /**
+     * Removes the value for the key from the array, returning a
+     * new array if necessary.
+     */
+    static public Object[] remove(Object[] array, Object key)
+    {
+        return remove(array, key, true);
+    }
+
+    /**
+     * Removes the value for the key from the array, returning a
+     * new array if necessary.
+     */
+    static public Object[] remove(Object[] array, Object key, boolean reallocate)
+    {
+        if (array != null)
+        {
+            int length = array.length;
+
+            for (int i = 0; i < length; i += 2)
+            {
+                Object curKey = array[i];
+
+                if (((curKey != null) && curKey.equals(key)) || (curKey == key))
+                {
+                    Object[] newArray = array;
+
+                    if (reallocate)
+                    {
+                        newArray = new Object[length - 2];
+                        System.arraycopy(array, 0, newArray, 0, i);
+                    }
+
+                    System.arraycopy(array, i + 2, newArray, i, length - i - 2);
+
+                    if (!reallocate)
+                    {
+                        array[length - 1] = null;
+                        array[length - 2] = null;
+                    }
+
+                    return newArray;
+                }
+            }
+        }
+
+        return array;
+    }
+
+    //
+    // GENERIC MAP API
+    //
+    @Override
+    public int size()
+    {
+        return _size;
+    }
+
+    @Override
+    public boolean containsValue(Object value)
+    {
+        int entryCount = size() * 2;
+        for (int i = 0; i < entryCount; i += 2)
+        {
+            if (_equals(value, _array[i + 1]))
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    public boolean containsKey(Object key)
+    {
+        int entryCount = size() * 2;
+        for (int i = 0; i < entryCount; i += 2)
+        {
+            if (_equals(key, _array[i]))
+            {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns an enumeration of the keys in this map.
+     * the Iterator methods on the returned object to fetch the elements
+     * sequentially.
+     */
+    @SuppressWarnings("unchecked")
+    public Iterator<K> keys()
+    {
+        int size = _size;
+
+        if (size == 0)
+        {
+            return null;
+        }
+        ArrayList<K> keyList = new ArrayList<K>();
+        int i = (size - 1) * 2;
+        while (i >= 0)
+        {
+            keyList.add((K) _array[i]);
+            i = i - 2;
+        }
+        return keyList.iterator();
+    }
+
+    /**
+     * Returns an Iterator of keys in the array.
+     */
+    public static Iterator<Object> getKeys(Object[] array)
+    {
+        if (array == null)
+        {
+            return null;
+        }
+        ArrayList<Object> keyList = new ArrayList<Object>();
+        int i = array.length - 2;
+        while (i >= 0)
+        {
+            keyList.add(array[i]);
+            i = i - 2;
+        }
+        return keyList.iterator();
+    }
+
+    /**
+     * Returns an Iterator of values in the array.
+     */
+    public static Iterator<Object> getValues(Object[] array)
+    {
+        if (array == null)
+        {
+            return null;
+        }
+        ArrayList<Object> valueList = new ArrayList<Object>();
+        int i = array.length - 1;
+        while (i >= 0)
+        {
+            valueList.add(array[i]);
+            i = i - 2;
+        }
+        return valueList.iterator();
+    }
+
+    /**
+     * Clones the map.
+     */
+    @SuppressWarnings("unchecked")
+    @Override
+    public Object clone()
+    {
+        try
+        {
+            _ArrayMap<K, V> am = (_ArrayMap<K, V>) super.clone();
+
+            am._array = _array.clone();
+            am._size = _size;
+            am._increment = _increment;
+            return am;
+        }
+        catch (CloneNotSupportedException cnse)
+        {
+            // Should never reach here
+            return null;
+        }
+    }
+
+    @Override
+    public Set<Map.Entry<K, V>> entrySet()
+    {
+        if (_entrySet == null)
+        {
+            _entrySet = new AbstractSet<Map.Entry<K, V>>()
+            {
+                @Override
+                public int size()
+                {
+                    return _ArrayMap.this.size();
+                }
+
+                @Override
+                public Iterator<Map.Entry<K, V>> iterator()
+                {
+                    return new Iterator<Map.Entry<K, V>>()
+                    {
+                        public boolean hasNext()
+                        {
+                            return (_index < _ArrayMap.this.size());
+                        }
+
+                        public void remove()
+                        {
+                            // remove() removes the last entry returned by next(),
+                            // not the one about to be seen;  so that's actually
+                            // the entry at (_index - 1).
+                            if ((_index == 0) || _removed)
+                            {
+                                throw new IllegalStateException();
+                            }
+
+                            _removed = true;
+                            // Shrink the array by one
+                            int size = _ArrayMap.this.size();
+                            Object[] array = _ArrayMap.this._array;
+                            if (size > _index)
+                            {
+                                System.arraycopy(array, _index * 2, array,
+                                        (_index - 1) * 2, (size - _index) * 2);
+                            }
+
+                            // Null out the last elements (for GC)
+                            array[size * 2 - 2] = null;
+                            array[size * 2 - 1] = null;
+
+                            _ArrayMap.this._size = size - 1;
+
+                            // And push the index back one
+                            _index = _index - 1;
+                        }
+
+                        public Map.Entry<K, V> next()
+                        {
+                            if (!hasNext())
+                            {
+                                throw new NoSuchElementException();
+                            }
+
+                            final int index = _index;
+                            _removed = false;
+                            _index = index + 1;
+
+                            return new Map.Entry<K, V>()
+                            {
+                                public K getKey()
+                                {
+                                    return _ArrayMap.this.getKey(index);
+                                }
+
+                                public V getValue()
+                                {
+                                    return _ArrayMap.this.getValue(index);
+                                }
+
+                                public V setValue(V value)
+                                {
+                                    V oldValue = getValue();
+                                    _ArrayMap.this._array[index * 2 + 1] = value;
+                                    return oldValue;
+                                }
+
+                                @SuppressWarnings("unchecked")
+                                @Override
+                                public boolean equals(Object o)
+                                {
+                                    if (!(o instanceof Map.Entry))
+                                    {
+                                        return false;
+                                    }
+                                    Map.Entry<K, V> e = (Map.Entry<K, V>) o;
+                                    return _equals(getKey(), e.getKey())
+                                            && _equals(getValue(), e.getValue());
+                                }
+
+                                @Override
+                                public int hashCode()
+                                {
+                                    Object key = getKey();
+                                    Object value = getValue();
+                                    return ((key == null) ? 0 : key.hashCode())
+                                            ^ ((value == null) ? 0 : value
+                                                    .hashCode());
+                                }
+                            };
+                        }
+
+                        private int _index;
+                        private boolean _removed;
+                    };
+                }
+            };
+        }
+
+        return _entrySet;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public V get(Object key)
+    {
+        return (V) getByEquality(_array, key);
+        //return getByIdentity(_array, key);
+    }
+
+    @SuppressWarnings("unchecked")
+    public V getByIdentity(Object key)
+    {
+        return (V) getByIdentity(_array, key);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public V put(K key, V value)
+    {
+        if (value == null)
+        {
+            return remove(key);
+        }
+
+        Object[] array = _array;
+        // Use getByEquality().  In the vast majority
+        // of cases, the object isn't there.  So getByIdentity()
+        // will fail, and we'll call getByEquality() anyway.
+        Object o = getByEquality(array, key);
+
+        if (o == null)
+        {
+            int size = _size * 2;
+            if ((array != null) && (size < array.length))
+            {
+                array[size] = key;
+                array[size + 1] = value;
+            }
+            else
+            {
+                _array = _addToArray(array, key, value, _increment);
+            }
+
+            _size = _size + 1;
+        }
+        else
+        {
+            // (Actually, I know in this case that the returned array
+            // isn't going to change, but that'd be a good way to introduce
+            // a bug if we ever to change the implementation)
+            _array = put(array, key, value);
+        }
+
+        return (V) o;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public V remove(Object key)
+    {
+        Object[] array = _array;
+        Object o = get(array, key);
+        if (o != null)
+        {
+            remove(array, key, false);
+            _size = _size - 1;
+        }
+
+        return (V) o;
+    }
+
+    /**
+     * Removes all elements from the ArrayMap.
+     */
+    @Override
+    public void clear()
+    {
+        int size = _size;
+        if (size > 0)
+        {
+            size = size * 2;
+            for (int i = 0; i < size; i++)
+            {
+                _array[i] = null;
+            }
+
+            _size = 0;
+        }
+    }
+
+    /**
+     * Adds the key/value pair to the array, returning a
+     * new array if necessary.
+     */
+    private static Object[] _addToArray(Object[] array, Object key,
+            Object value, int increment)
+    {
+        Object[] newArray = null;
+        if (array != null)
+        {
+            int length = array.length;
+            newArray = new Object[length + (2 * increment)];
+            System.arraycopy(array, 0, newArray, 2, length);
+        }
+        else
+        {
+            newArray = new Object[2 * increment];
+        }
+
+        newArray[0] = key;
+        newArray[1] = value;
+        return newArray;
+    }
+
+    private static boolean _equals(Object a, Object b)
+    {
+        if (a == null)
+        {
+            return b == null;
+        }
+        return a.equals(b);
+    }
+
+    private Object[] _array;
+    private int _size;
+    private int _increment;
+    private Set<Map.Entry<K, V>> _entrySet;
+}

Propchange: myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/_ArrayMap.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/_ComponentFacetMap.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/_ComponentFacetMap.java?rev=1297574&r1=1297573&r2=1297574&view=diff
==============================================================================
--- myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/_ComponentFacetMap.java (original)
+++ myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/_ComponentFacetMap.java Tue Mar  6 16:58:54 2012
@@ -22,7 +22,6 @@ import java.io.Serializable;
 import java.util.AbstractCollection;
 import java.util.AbstractSet;
 import java.util.Collection;
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
@@ -35,7 +34,7 @@ class _ComponentFacetMap<V extends UICom
 {
     private static final long serialVersionUID = -3456937594422167629L;
     private UIComponent _component;
-    private Map<String, V> _map = new HashMap<String, V>();
+    private Map<String, V> _map = new _ArrayMap<String, V>(0,5);
     private Set<Entry<String, V>> _entrySet = null;
     private Set<String> _keySet = null;
     private Collection<V> _valueCollection = null;

Modified: myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/behavior/BehaviorBase.java
URL: http://svn.apache.org/viewvc/myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/behavior/BehaviorBase.java?rev=1297574&r1=1297573&r2=1297574&view=diff
==============================================================================
--- myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/behavior/BehaviorBase.java (original)
+++ myfaces/core/branches/2.0.x/api/src/main/java/javax/faces/component/behavior/BehaviorBase.java Tue Mar  6 16:58:54 2012
@@ -267,8 +267,9 @@ public class BehaviorBase implements Beh
         
         if (_behaviorListeners == null)
         {
-            // Lazy instanciation
-            _behaviorListeners = new _DeltaList<BehaviorListener>(new ArrayList<BehaviorListener>());
+            // Lazy instanciation with size 1:
+            // the only posibility how to add listener is <f:ajax listener="" /> - there is no <f:ajaxListener/> tag 
+            _behaviorListeners = new _DeltaList<BehaviorListener>(new ArrayList<BehaviorListener>(1));
         }
         
         _behaviorListeners.add(listener);