You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2006/05/24 04:56:20 UTC

svn commit: r409046 - /incubator/cayenne/main/trunk/cayenne/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/ObjectId.java

Author: aadamchik
Date: Tue May 23 19:56:20 2006
New Revision: 409046

URL: http://svn.apache.org/viewvc?rev=409046&view=rev
Log:
CAY-525 - optimizing the most common ObjectId case - a single key OID. Performance of hashCode and constructor is improved ~2x to 3x

Modified:
    incubator/cayenne/main/trunk/cayenne/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/ObjectId.java

Modified: incubator/cayenne/main/trunk/cayenne/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/ObjectId.java
URL: http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/cayenne/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/ObjectId.java?rev=409046&r1=409045&r2=409046&view=diff
==============================================================================
--- incubator/cayenne/main/trunk/cayenne/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/ObjectId.java (original)
+++ incubator/cayenne/main/trunk/cayenne/cayenne-java/src/cayenne/java/org/objectstyle/cayenne/ObjectId.java Tue May 23 19:56:20 2006
@@ -80,6 +80,10 @@
 
     protected String entityName;
     protected Map objectIdKeys;
+    
+    private String singleKey;
+    private Object singleValue; 
+    
 
     protected byte[] key;
 
@@ -148,10 +152,8 @@
     public ObjectId(String entityName, String key, Object value) {
         this.entityName = entityName;
 
-        // don't use Collections.singletonMap() as we need a mutable single level map
-        // internally (for things like Hessian serialization).
-        this.objectIdKeys = new HashMap(1);
-        objectIdKeys.put(key, value);
+        this.singleKey = key;
+        this.singleValue = value;
     }
 
     /**
@@ -162,9 +164,21 @@
     public ObjectId(String entityName, Map idMap) {
         this.entityName = entityName;
 
-        // we have to create a copy of the map, otherwise we may run into serialization
-        // problems with hessian
-        this.objectIdKeys = idMap != null && !idMap.isEmpty() ? new HashMap(idMap) : null;
+        if (idMap == null || idMap.size() == 0) {
+
+        }
+        else if (idMap.size() == 1) {
+            Map.Entry e = (Map.Entry) idMap.entrySet().iterator().next();
+            this.singleKey = String.valueOf(e.getKey());
+            this.singleValue = e.getValue();
+        }
+        else {
+
+            // we have to create a copy of the map, otherwise we may run into
+            // serialization
+            // problems with hessian
+            this.objectIdKeys = new HashMap(idMap);
+        }
     }
 
     /**
@@ -226,7 +240,14 @@
      * @deprecated since 1.2
      */
     protected void setIdKeys(Map idKeys) {
-        this.objectIdKeys = idKeys;
+        if (idKeys != null && idKeys.size() == 1) {
+            Map.Entry e = (Map.Entry) idKeys.entrySet().iterator().next();
+            this.singleKey = String.valueOf(e.getKey());
+            this.singleValue = e.getValue();
+        }
+        else {
+            this.objectIdKeys = idKeys;
+        }
     }
 
     /**
@@ -240,6 +261,10 @@
                     .unmodifiableMap(replacementIdMap);
         }
 
+        if(singleKey != null) {
+            return Collections.singletonMap(singleKey, singleValue);
+        }
+        
         return objectIdKeys != null
                 ? Collections.unmodifiableMap(objectIdKeys)
                 : Collections.EMPTY_MAP;
@@ -274,19 +299,19 @@
             return new EqualsBuilder().append(key, id.key).isEquals();
         }
 
-        if (id.objectIdKeys == null && objectIdKeys == null) {
-            return true;
+        if (singleKey != null) {
+            return Util.nullSafeEquals(singleKey, id.singleKey)
+                    && valueEquals(singleValue, id.singleValue);
         }
 
-        if (id.objectIdKeys == null || objectIdKeys == null) {
-            return false;
+        if (id.objectIdKeys == null) {
+            return objectIdKeys == null;
         }
 
         if (id.objectIdKeys.size() != objectIdKeys.size()) {
             return false;
         }
 
-        EqualsBuilder builder = new EqualsBuilder();
         Iterator entries = objectIdKeys.entrySet().iterator();
         while (entries.hasNext()) {
             Map.Entry entry = (Map.Entry) entries.next();
@@ -300,31 +325,35 @@
                 }
             }
             else {
-                Object otherValue = id.objectIdKeys.get(entryKey);
-
-                // normalize all numeric types
-                if (entryValue instanceof Number) {
-                    if (!(otherValue instanceof Number)) {
-                        return false;
-                    }
-
-                    if (((Number) entryValue).longValue() != ((Number) otherValue)
-                            .longValue()) {
-                        return false;
-                    }
-                }
-                else {
-                    // takes care of comparing primitive arrays, such as byte[]
-                    builder.append(entryValue, otherValue);
-                    if (!builder.isEquals()) {
-                        return false;
-                    }
+                if (!valueEquals(entryValue, id.objectIdKeys.get(entryKey))) {
+                    return false;
                 }
             }
         }
 
         return true;
     }
+    
+    private final boolean valueEquals(Object o1, Object o2) {
+        if (o1 == o2) {
+            return true;
+        }
+
+        if (o1 == null) {
+            return o2 == null;
+        }
+
+        if (o1 instanceof Number) {
+            return o2 instanceof Number
+                    && ((Number) o1).longValue() == ((Number) o2).longValue();
+        }
+
+        if (o1.getClass().isArray()) {
+            return new EqualsBuilder().append(o1, o2).isEquals();
+        }
+
+        return Util.nullSafeEquals(o1, o2);
+    }
 
     public int hashCode() {
 
@@ -336,45 +365,40 @@
             if (key != null) {
                 builder.append(key);
             }
+            else if(singleKey != null) {
+                builder.append(singleKey);
+
+                // must reconcile all possible numeric types
+                if (singleValue instanceof Number) {
+                    builder.append(((Number) singleValue).longValue());
+                }
+                else {
+                    builder.append(singleValue);
+                }
+            }
             else if (objectIdKeys != null) {
                 int len = objectIdKeys.size();
 
-                // handle cheap and most common case - single key
-                if (len == 1) {
-                    Iterator entries = objectIdKeys.entrySet().iterator();
-                    Map.Entry entry = (Map.Entry) entries.next();
+                // handle multiple keys - must sort the keys to use with HashCodeBuilder
 
-                    builder.append(entry.getKey());
+                Object[] keys = objectIdKeys.keySet().toArray();
+                Arrays.sort(keys);
 
+                for (int i = 0; i < len; i++) {
+                    // HashCodeBuilder will take care of processing object if it
+                    // happens to be a primitive array such as byte[]
+
+                    // also we don't have to append the key hashcode, its index will
+                    // work
+                    builder.append(i);
+
+                    Object value = objectIdKeys.get(keys[i]);
                     // must reconcile all possible numeric types
-                    if (entry.getValue() instanceof Number) {
-                        builder.append(((Number) entry.getValue()).longValue());
+                    if (value instanceof Number) {
+                        builder.append(((Number) value).longValue());
                     }
                     else {
-                        builder.append(entry.getValue());
-                    }
-                }
-                // handle multiple keys - must sort the keys to use with HashCodeBuilder
-                else {
-                    Object[] keys = objectIdKeys.keySet().toArray();
-                    Arrays.sort(keys);
-
-                    for (int i = 0; i < len; i++) {
-                        // HashCodeBuilder will take care of processing object if it
-                        // happens to be a primitive array such as byte[]
-
-                        // also we don't have to append the key hashcode, its index will
-                        // work
-                        builder.append(i);
-
-                        Object value = objectIdKeys.get(keys[i]);
-                        // must reconcile all possible numeric types
-                        if (value instanceof Number) {
-                            builder.append(((Number) value).longValue());
-                        }
-                        else {
-                            builder.append(value);
-                        }
+                        builder.append(value);
                     }
                 }
             }
@@ -459,6 +483,10 @@
             for (int i = 0; i < key.length; i++) {
                 IDUtil.appendFormattedByte(buffer, key[i]);
             }
+        }
+        else if(singleKey != null) {
+            buffer.append(String.valueOf(singleKey)).append("=").append(
+                    singleValue);
         }
         else if (objectIdKeys != null) {
             Iterator it = objectIdKeys.entrySet().iterator();