Java/Class/Equals — различия между версиями

Материал из Java эксперт
Перейти к: навигация, поиск
 
м (1 версия)
 
(нет различий)

Текущая версия на 06:36, 1 июня 2010

Compares two objects for equality, where either one or both objects may be null

 
/*
 * 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.
 */
import java.io.Serializable;
/**
 * <p>Operations on <code>Object</code>.</p>
 * 
 * <p>This class tries to handle <code>null</code> input gracefully.
 * An exception will generally not be thrown for a <code>null</code> input.
 * Each method documents its behaviour in more detail.</p>
 *
 * @author 
 * @since 1.0
 * @version $Id: ObjectUtils.java 594336 2007-11-12 22:54:02Z bayard $
 */
public class ObjectUtils {
    /**
     * <p>Singleton used as a <code>null</code> placeholder where
     * <code>null</code> has another meaning.</p>
     *
     * <p>For example, in a <code>HashMap</code> the
     * {@link java.util.HashMap#get(java.lang.Object)} method returns
     * <code>null</code> if the <code>Map</code> contains
     * <code>null</code> or if there is no matching key. The
     * <code>Null</code> placeholder can be used to distinguish between
     * these two cases.</p>
     *
     * <p>Another example is <code>Hashtable</code>, where <code>null</code>
     * cannot be stored.</p>
     *
     * <p>This instance is Serializable.</p>
     */
    public static final Null NULL = new Null();
    
    /**
     * <p><code>ObjectUtils</code> instances should NOT be constructed in
     * standard programming. Instead, the class should be used as
     * <code>ObjectUtils.defaultIfNull("a","b");</code>.</p>
     *
     * <p>This constructor is public to permit tools that require a JavaBean instance
     * to operate.</p>
     */
    public ObjectUtils() {
        super();
    }
    // Defaulting
    //-----------------------------------------------------------------------
    /**
     * <p>Returns a default value if the object passed is
     * <code>null</code>.</p>
     * 
     * <pre>
     * ObjectUtils.defaultIfNull(null, null)      = null
     * ObjectUtils.defaultIfNull(null, "")        = ""
     * ObjectUtils.defaultIfNull(null, "zz")      = "zz"
     * ObjectUtils.defaultIfNull("abc", *)        = "abc"
     * ObjectUtils.defaultIfNull(Boolean.TRUE, *) = Boolean.TRUE
     * </pre>
     *
     * @param object  the <code>Object</code> to test, may be <code>null</code>
     * @param defaultValue  the default value to return, may be <code>null</code>
     * @return <code>object</code> if it is not <code>null</code>, defaultValue otherwise
     */
    public static Object defaultIfNull(Object object, Object defaultValue) {
        return object != null ? object : defaultValue;
    }
    /**
     * <p>Compares two objects for equality, where either one or both
     * objects may be <code>null</code>.</p>
     *
     * <pre>
     * ObjectUtils.equals(null, null)                  = true
     * ObjectUtils.equals(null, "")                    = false
     * ObjectUtils.equals("", null)                    = false
     * ObjectUtils.equals("", "")                      = true
     * ObjectUtils.equals(Boolean.TRUE, null)          = false
     * ObjectUtils.equals(Boolean.TRUE, "true")        = false
     * ObjectUtils.equals(Boolean.TRUE, Boolean.TRUE)  = true
     * ObjectUtils.equals(Boolean.TRUE, Boolean.FALSE) = false
     * </pre>
     *
     * @param object1  the first object, may be <code>null</code>
     * @param object2  the second object, may be <code>null</code>
     * @return <code>true</code> if the values of both objects are the same
     */
    public static boolean equals(Object object1, Object object2) {
        if (object1 == object2) {
            return true;
        }
        if ((object1 == null) || (object2 == null)) {
            return false;
        }
        return object1.equals(object2);
    }
    /**
     * <p>Gets the hash code of an object returning zero when the
     * object is <code>null</code>.</p>
     *
     * <pre>
     * ObjectUtils.hashCode(null)   = 0
     * ObjectUtils.hashCode(obj)    = obj.hashCode()
     * </pre>
     *
     * @param obj  the object to obtain the hash code of, may be <code>null</code>
     * @return the hash code of the object, or zero if null
     * @since 2.1
     */
    public static int hashCode(Object obj) {
        return (obj == null) ? 0 : obj.hashCode();
    }
    // Identity ToString
    //-----------------------------------------------------------------------
    /**
     * <p>Gets the toString that would be produced by <code>Object</code>
     * if a class did not override toString itself. <code>null</code>
     * will return <code>null</code>.</p>
     *
     * <pre>
     * ObjectUtils.identityToString(null)         = null
     * ObjectUtils.identityToString("")           = "java.lang.String@1e23"
     * ObjectUtils.identityToString(Boolean.TRUE) = "java.lang.Boolean@7fa"
     * </pre>
     *
     * @param object  the object to create a toString for, may be
     *  <code>null</code>
     * @return the default toString text, or <code>null</code> if
     *  <code>null</code> passed in
     */
    public static String identityToString(Object object) {
        if (object == null) {
            return null;
        }
        StringBuffer buffer = new StringBuffer();
        identityToString(buffer, object);
        return buffer.toString();
    }
    /**
     * <p>Appends the toString that would be produced by <code>Object</code>
     * if a class did not override toString itself. <code>null</code>
     * will throw a NullPointerException for either of the two parameters. </p>
     *
     * <pre>
     * ObjectUtils.identityToString(buf, "")            = buf.append("java.lang.String@1e23"
     * ObjectUtils.identityToString(buf, Boolean.TRUE)  = buf.append("java.lang.Boolean@7fa"
     * ObjectUtils.identityToString(buf, Boolean.TRUE)  = buf.append("java.lang.Boolean@7fa")
     * </pre>
     *
     * @param buffer  the buffer to append to
     * @param object  the object to create a toString for
     * @since 2.4
     */
    public static void identityToString(StringBuffer buffer, Object object) {
        if (object == null) {
            throw new NullPointerException("Cannot get the toString of a null identity");
        }
        buffer.append(object.getClass().getName())
              .append("@")
              .append(Integer.toHexString(System.identityHashCode(object)));
    }

    // ToString
    //-----------------------------------------------------------------------
    /**
     * <p>Gets the <code>toString</code> of an <code>Object</code> returning
     * an empty string ("") if <code>null</code> input.</p>
     * 
     * <pre>
     * ObjectUtils.toString(null)         = ""
     * ObjectUtils.toString("")           = ""
     * ObjectUtils.toString("bat")        = "bat"
     * ObjectUtils.toString(Boolean.TRUE) = "true"
     * </pre>
     * 
     * @see StringUtils#defaultString(String)
     * @see String#valueOf(Object)
     * @param obj  the Object to <code>toString</code>, may be null
     * @return the passed in Object"s toString, or nullStr if <code>null</code> input
     * @since 2.0
     */
    public static String toString(Object obj) {
        return obj == null ? "" : obj.toString();
    }
    /**
     * <p>Gets the <code>toString</code> of an <code>Object</code> returning
     * a specified text if <code>null</code> input.</p>
     * 
     * <pre>
     * ObjectUtils.toString(null, null)           = null
     * ObjectUtils.toString(null, "null")         = "null"
     * ObjectUtils.toString("", "null")           = ""
     * ObjectUtils.toString("bat", "null")        = "bat"
     * ObjectUtils.toString(Boolean.TRUE, "null") = "true"
     * </pre>
     * 
     * @see StringUtils#defaultString(String,String)
     * @see String#valueOf(Object)
     * @param obj  the Object to <code>toString</code>, may be null
     * @param nullStr  the String to return if <code>null</code> input, may be null
     * @return the passed in Object"s toString, or nullStr if <code>null</code> input
     * @since 2.0
     */
    public static String toString(Object obj, String nullStr) {
        return obj == null ? nullStr : obj.toString();
    }

    // Null
    //-----------------------------------------------------------------------
    /**
     * <p>Class used as a null placeholder where <code>null</code>
     * has another meaning.</p>
     *
     * <p>For example, in a <code>HashMap</code> the
     * {@link java.util.HashMap#get(java.lang.Object)} method returns
     * <code>null</code> if the <code>Map</code> contains
     * <code>null</code> or if there is no matching key. The
     * <code>Null</code> placeholder can be used to distinguish between
     * these two cases.</p>
     *
     * <p>Another example is <code>Hashtable</code>, where <code>null</code>
     * cannot be stored.</p>
     */
    public static class Null implements Serializable {
        /**
         * Required for serialization support. Declare serialization compatibility with Commons Lang 1.0
         * 
         * @see java.io.Serializable
         */
        private static final long serialVersionUID = 7092611880189329093L;
        
        /**
         * Restricted constructor - singleton.
         */
        Null() {
            super();
        }
        
        /**
         * <p>Ensure singleton.</p>
         * 
         * @return the singleton value
         */
        private Object readResolve() {
            return ObjectUtils.NULL;
        }
    }
}





Equals(Equal) Method

  
//: c03:EqualsMethod2.java
// From "Thinking in Java, 3rd ed." (c) Bruce Eckel 2002
// www.BruceEckel.ru. See copyright notice in CopyRight.txt.

class Value {
  int i;
}
public class EqualsMethod2 {
  public static void main(String[] args) {
    Value v1 = new Value();
    Value v2 = new Value();
    v1.i = v2.i = 100;
    System.out.println(v1.equals(v2));
  }
} ///:~





Equals Method

  
//: c03:EqualsMethod.java
// From "Thinking in Java, 3rd ed." (c) Bruce Eckel 2002
// www.BruceEckel.ru. See copyright notice in CopyRight.txt.

public class EqualsMethod {
  public static void main(String[] args) {
    Integer n1 = new Integer(47);
    Integer n2 = new Integer(47);
    System.out.println(n1.equals(n2));
  }
} ///:~





Equivalence

  
//: c03:Equivalence.java
// From "Thinking in Java, 3rd ed." (c) Bruce Eckel 2002
// www.BruceEckel.ru. See copyright notice in CopyRight.txt.

public class Equivalence {
  static Test monitor = new Test();
  public static void main(String[] args) {
    Integer n1 = new Integer(47);
    Integer n2 = new Integer(47);
    System.out.println(n1 == n2);
    System.out.println(n1 != n2);
  }
} ///:~





Equivalence ClassSet

    
/**
 * EquivalenceClassSet.java
 * 
 *  @author bret5
 *  Copyright(C) 2007 bret5
 *  
 *  This is a specialized set class which groups the elements into
 *  equivalence classes based on the by the comparator provided at 
 *  set creation time.  Iterators created thru the standard set interface
 *  will return elements sorted by comparator order.  Elements that
 *  are equivalent may be returned in any order.
 *  
 *  The class also provides a special loopIterator iteration 
 *  which will return each element once in comparator order, then loop back
 *  around to the first element at the end.  The loopIterator continues to be valid
 *  as add/remove operations are performed.  If shuffleEquivalenceClasses is set,
 *  it will randomly shuffles the elements in each equivalence class 
 *  every time that equivalence class is reached during iteration.  If not,
 *  each equivalence class will be returned in the same order every time.
 *  shuffleEquivalenceClasses defaults to true.
 *  
 *  This Set does not allow null elements.
 *  
 *  This was written for a program that uses jdk 1.4, so it doesn"t yet use the generic style.  
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.ruparator;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;
/**
 * @author bret5
 *
 */
public class EquivalenceClassSet<T> extends AbstractSet<T> 
{
    // the list backing the class.  This is a list of the 
    // equivalence classes - each equivalence class is a sublist
    // which contains all the elements equivalent to each other.
    // The sublists appear in order corresponding to the ordering
    // provided by the comparator provided at set creation time. 
    private List                m_equivalenceClasses;
    private int                 m_size; // cached for efficiency
    private Comparator          m_comparator;
    private ListIterator        m_loopEqvClassIter; // non-null index used by the loop iterator
    private List                m_loopCurrentEqvClass; // ptr to current class, null ok
    private ListIterator<T>     m_loopItemIter; // non-null index used by the loop iterator
    private final static List   m_emptyList = new ArrayList(); // emptyList used for gen-ing iterators
    
    // In order to maintain some sanity in the face of objects changing with respect to
    // the comparator after being added to the set, we keep track of which class every object
    // is in.  This way, the behavior of contains and remove are undisturbed by changes to the 
    // objects.
    private HashMap         m_itemToClassMap;
    
    // TODO - size is now redundant with the size of the item-class map, remove...
    
    private int m_changeID; // supports iterator fail-fast, increment on each modification
    private boolean m_shuffleEquivalenceClasses;
    
    /*
     * Class invariants:
     *   Individual equivalence classes may not be empty
     *   The cached size is equal to the sum of all items in the sublists
     *   The changeID increments montonically whenever the set contents are changed,
     *   although it does reset on a clear()
     */
    
    public EquivalenceClassSet(Comparator<T> c) 
    {
        super();
        m_comparator = c;
        m_equivalenceClasses = new LinkedList();
        m_size = 0;
        m_itemToClassMap = new HashMap();
        resetLoopIterator();
        m_changeID = 0;
        m_shuffleEquivalenceClasses = true;
    }
    protected class OnePassIterator implements Iterator<T> 
    {
        private int localChangeID;
        private ListIterator localEqvClassIter; // class iter
        private ListIterator<T> localItemIter; // item iter
        protected OnePassIterator() 
        {
            localChangeID = m_changeID;
            localEqvClassIter = m_equivalenceClasses.listIterator(); // index used by the loop iterator
            localItemIter = null; // null means before the next class
        }
        
        public boolean hasNext() 
        {
            if (m_changeID != localChangeID) 
            {
                throw new ConcurrentModificationException();
            }
            
            if (localEqvClassIter.hasNext() || 
                    (localItemIter != null && localItemIter.hasNext())) 
            {
                return true;
            }
            
            return false;
        }
        public T next() 
        {
            if (m_changeID != localChangeID) 
            {
                throw new ConcurrentModificationException();
            }
            
            if (localItemIter == null || !localItemIter.hasNext()) 
            {
                localItemIter = ((List)localEqvClassIter.next()).listIterator();
            }
            
            return localItemIter.next();
        }
        public void remove() 
        {
            throw new UnsupportedOperationException();
        }            
    }
    
    public Iterator<T> iterator() 
    {
        return new OnePassIterator();
    }
    
    // Note that this iterator is intended to continue iteration after element addition 
    // and removal.  Therefore we put the underlying iterators in the main class so that 
    // they can be adjusted/replaced when necessary
    // invariants:
    //   if size = 0, currentEqvClassIter is null.
    //   if size not null, then both currentEqvClassIter and currentItemIter
    //     are not null.
    protected class LoopIterator implements Iterator<T> 
    {
        public boolean hasNext() 
        {
            return ( m_size > 0 );
        }
        public T next() 
        {
            assert m_loopEqvClassIter != null;
            assert m_loopItemIter != null;
            
            if (m_size <= 0) 
            {
                throw new NoSuchElementException();
            }
            
            // first, move to a new class if necessary, resetting the item iter.
            // if switching classes, shuffle.
            if (!m_loopItemIter.hasNext()) 
            {
                if (!m_loopEqvClassIter.hasNext()) 
                {
                    // recycle to beginning
                    m_loopEqvClassIter = m_equivalenceClasses.listIterator();
                }
                
                m_loopCurrentEqvClass = (List)m_loopEqvClassIter.next();
                assert m_loopCurrentEqvClass.size() > 0;
                
                if (m_shuffleEquivalenceClasses) 
                {
                    java.util.Collections.shuffle(m_loopCurrentEqvClass);
                }
                m_loopItemIter = m_loopCurrentEqvClass.listIterator();                
            }
            
            return m_loopItemIter.next();
        }
        public void remove() 
        {
            throw new UnsupportedOperationException();
        }                               
    }
    
    /**
     * The loopIterator is a special iteration for which hasNext() returns 
     * true if the size is greater than 0.  The next() method 
     * traverses the equivalence classes in comparator order.  Within
     * each equivalence class, the items are returned randomly 
     * (by shuffling the elements in the equivalence class every time 
     * that equivalence class is reached during iteration).
     * 
     * Iteration can be reset to the first equivalence class by using
     * the resetLoopIterator method of the main class.
     *   
     * @return the iterator
     */
    public Iterator<T> loopIterator() 
    {
        return new LoopIterator();
    }
   
    public void resetLoopIterator() 
    {
        m_loopEqvClassIter = m_equivalenceClasses.listIterator();
        m_loopCurrentEqvClass = null;
        m_loopItemIter = m_emptyList.listIterator();        
    }
    
    /**
     * If shuffleEquivalenceClasses is set, the loopItertor will randomly shuffle 
     * the elements in each equivalence class every time that equivalence class 
     * is reached during iteration.
     * 
     * @return the value of shuffleEquivalenceClasses
     */
    public boolean isShuffleEquivalenceClasses() 
    {
        return m_shuffleEquivalenceClasses;
    }
    /**
     * Set the value of shuffleEquivalenceClasses.
     * @see isShuffleEquivalenceClasses()
     */
    public void setShuffleEquivalenceClasses(boolean shuffleEquivalenceClasses) 
    {
        this.m_shuffleEquivalenceClasses = shuffleEquivalenceClasses;
    }
    /* (non-Javadoc)
     * @see java.util.AbstractCollection#size()
     */
    public int size() 
    {
        return m_size;
    }
    /* 
     * Adds the argument to the collection.  If the element is not already
     * a member of the set, and is equivalent to the current equivalence 
     * class, it is added so that it will be returned before any element
     * from another class is returned.
     */
    public boolean add(T arg0) 
    {
        return addPositional(arg0, true);
    }
    /* 
     * Adds the argument to the collection.  If the element is not already
     * a member of the set, and is equivalent to the current equivalence 
     * class, it is added so that it will not be returned before an element
     * from another class is returned (if there are any other classes).
     */
    public boolean addExpired(T arg0) 
    {
        return addPositional(arg0, false);
    }
    protected boolean addPositional(Object arg0, boolean atEnd) 
    {
        EqvPosition eqvPosition = findEqvClass(arg0);
        boolean isChanged = false;
        if (eqvPosition.matchingEqvClass != null) 
        {
            int iterIdx = 0;
            boolean replaceLoopItemIter = false;
            
            if (eqvPosition.matchingEqvClass == m_loopCurrentEqvClass) 
            {
                // we have to replace the item loop iterator for this class, so get the position
                replaceLoopItemIter = true;
                iterIdx = m_loopItemIter.nextIndex();
            }
            
            if (!eqvPosition.matchingEqvClass.contains(arg0)) 
            {
                if (atEnd) 
                {
                    eqvPosition.matchingEqvClass.add(arg0);
                } 
                else
                {
                    eqvPosition.matchingEqvClass.add(0, arg0);
                    iterIdx += 1;
                }
                
                isChanged = true;
                
                if (replaceLoopItemIter) 
                {
                    m_loopItemIter = m_loopCurrentEqvClass.listIterator(iterIdx);
                }
            }
        } 
        else 
        {
            // there is no matching class, so add one
            ArrayList newEqvClass = new ArrayList();
            newEqvClass.add(arg0);
            eqvPosition.matchingEqvClass = newEqvClass;  // cache the eqv class ref for adding to map
            int iterIdx, addIdx;
            iterIdx = 0;
            addIdx = 0;
            
            if (m_size >= 1) 
            {
                iterIdx = m_loopEqvClassIter.nextIndex();
                addIdx = eqvPosition.eqvClassIter.nextIndex();
                if (addIdx < iterIdx) 
                {
                    iterIdx += 1;
                }
            } 
            eqvPosition.eqvClassIter.add(newEqvClass);                
            isChanged = true;
            
            // replace the class loop iterator
            m_loopEqvClassIter = m_equivalenceClasses.listIterator(iterIdx);
            
            // if the new class is next and the add is "expired"/(not atEnd), 
            // advance the iterator past the just added item
            if (iterIdx == addIdx && !atEnd && !m_loopItemIter.hasNext()) 
            {
                m_loopCurrentEqvClass = (List)m_loopEqvClassIter.next();
                m_loopItemIter = m_loopCurrentEqvClass.listIterator(1);
            }
        }
        
        if (isChanged) 
        {
            m_size += 1;
            m_changeID += 1;
            m_itemToClassMap.put(arg0, eqvPosition.matchingEqvClass);
        }
        
        return isChanged;
    }
    // represents the location of the equivalence class matching a value.
    // returned from findEqvClass, so we only have to write that code once.
    // If there is a matching class, then matchingEqvClass will be non-null.
    // If not, then eqvClassIter holds the position that eqv class would have.
    protected class EqvPosition 
    {
        protected List matchingEqvClass;
        protected ListIterator eqvClassIter;
    }
    // If there is a matching class, then matchingEqvClass will be non-null.
    // If not, then eqvClassIter holds the position that eqv class would have.
    protected EqvPosition findEqvClass(Object arg0) 
    {
        EqvPosition eqvPosition = new EqvPosition();
        if (m_itemToClassMap.containsKey(arg0)) 
        {
            eqvPosition.matchingEqvClass = (List)m_itemToClassMap.get(arg0);
            return eqvPosition;  // note that the iterator will be null.
        }
        
        eqvPosition.eqvClassIter = m_equivalenceClasses.listIterator();
        while (eqvPosition.eqvClassIter.hasNext()) 
        {
            List testEqvClass = (List)eqvPosition.eqvClassIter.next();
            assert testEqvClass.size() > 0;
            int comparison = m_comparator.rupare(arg0, testEqvClass.get(0));
            if (comparison < 0) 
            {
                // there is no matching class, to insert before this one, return the previous position
                if (eqvPosition.eqvClassIter.hasPrevious()) 
                {
                    eqvPosition.eqvClassIter.previous();
                }
                break;
            } 
            else if (comparison == 0) 
            {
                eqvPosition.matchingEqvClass = testEqvClass;
                return eqvPosition; 
            }
            // and fall through to the next eqvClass
        }
        return eqvPosition;
    }
    
    /* (non-Javadoc)
     * @see java.util.AbstractCollection#clear()
     */
    public void clear() 
    {
        m_equivalenceClasses.clear();
        m_itemToClassMap.clear();
        m_size = 0;
        m_changeID = 0; // can reset to original state
        resetLoopIterator();
    }
    /* (non-Javadoc)
     * @see java.util.AbstractCollection#contains(java.lang.Object)
     */
    public boolean contains(Object arg0) 
    {
        EqvPosition eqvPosition = findEqvClass(arg0);
        if (eqvPosition.matchingEqvClass != null &&
                eqvPosition.matchingEqvClass.contains(arg0)) 
        {
            return true; // already a member, do nothing
        }   
        return false;
    }
    /* (non-Javadoc)
     * @see java.util.AbstractCollection#remove(java.lang.Object)
     */
    public boolean remove(Object arg0) 
    {
        assert m_size == m_itemToClassMap.size();
        EqvPosition eqvPosition = findEqvClass(arg0);
        return removeAtPosition(eqvPosition, arg0);
    }
    /* (non-Javadoc)
     * @see java.util.AbstractCollection#remove(java.lang.Object)
     */
    private boolean removeAtPosition(EqvPosition eqvPosition, Object arg0) 
    {
        // when removing an object, we may have to replace the item loop iterator
        // if we removed an item from the current loop class
        // also, if the removal results in removal of a class, we may have to 
        // replace the class loop iter 
        boolean isChanged = false;
        boolean replaceLoopItemIter = false;
        if (eqvPosition.matchingEqvClass != null) 
        {
            int itemLocationIdx = 0;
            int loopNextItemIdx = 0;
            int itemClassIdx = 0;
            int loopNextClassIdx = 0;
            itemLocationIdx = eqvPosition.matchingEqvClass.indexOf(arg0);
            if (itemLocationIdx >= 0) 
            {
                // the item is a member of this class and will be removed 
                isChanged = true; 
                if (eqvPosition.matchingEqvClass == m_loopCurrentEqvClass) 
                {
                    // we may have to replace the item loop iterator for this class, so get the position
                    replaceLoopItemIter = true;
                    loopNextItemIdx = m_loopItemIter.nextIndex();
                    if (itemLocationIdx < loopNextItemIdx) 
                    {
                        loopNextItemIdx -= 1;
                    }
                }
                eqvPosition.matchingEqvClass.remove(arg0);
                if (eqvPosition.matchingEqvClass.size() <= 0) 
                {
                    // the class is now empty, remove it
                    loopNextClassIdx = m_loopEqvClassIter.nextIndex();
                    itemClassIdx = m_equivalenceClasses.indexOf(eqvPosition.matchingEqvClass);
                    if (itemClassIdx < loopNextClassIdx) 
                    {
                        loopNextClassIdx -= 1;
                    }
                    
                    m_equivalenceClasses.remove(eqvPosition.matchingEqvClass);
                    
                    // and replace the loop iterator, and maybe the item iterator 
                    if (m_equivalenceClasses.size() == 0) 
                    {
                        resetLoopIterator();
                    } 
                    else 
                    {                        
                        m_loopEqvClassIter = m_equivalenceClasses.listIterator(loopNextClassIdx);
                        if (eqvPosition.matchingEqvClass == m_loopCurrentEqvClass) 
                        {
                            m_loopCurrentEqvClass = null;
                            m_loopItemIter = m_emptyList.listIterator(); 
                        }                        
                    }
                } 
                else if (replaceLoopItemIter) 
                {
                    // replace the item iterator for the class
                    m_loopItemIter = m_loopCurrentEqvClass.listIterator(loopNextItemIdx); 
                }   
            }
        }
        
        if (isChanged) 
        {
            m_itemToClassMap.remove(arg0);
            m_size -= 1;
            assert m_size >= 0;
            assert m_size == m_itemToClassMap.size();
            m_changeID += 1;
        }
        
        return isChanged;
    }
    
    public Comparator<T> getComparator() 
    {
        return m_comparator;
    }
    
    /**
     * Partition this set into two sets, returning the new one.
     * 
     * The argument specifies how many elements to put in the new set.  Elements will be 
     * chosen in comparator order.  All elements put into the new set will be removed from 
     * this set.  If the original set contained less elements than the argument, then
     * after the partition the new set will contain all the elements and the original set
     * will be empty.
     *  
     * If the partition is non-trivial (that is, if the new set contains at least one 
     * element), then the counters for the loop iterator will be reset.
     *  
     * @param numberToRemove number of elements to remove from the original set
     * 
     * @return the new set
     */
    public EquivalenceClassSet<T> partition(int numberToRemove) 
    {
        EquivalenceClassSet<T> newSet = new EquivalenceClassSet<T>(m_comparator);
        while (numberToRemove > 0 && m_size > 0) 
        {
            ArrayList firstEqvClass = (ArrayList)(m_equivalenceClasses.get(0));
            int sizeOfFEqvClass = firstEqvClass.size();
            int numberMoved = 0;
            List movedEqvClass;
            if (numberToRemove >= sizeOfFEqvClass) 
            {
                movedEqvClass = (List)m_equivalenceClasses.remove(0);
                newSet.m_equivalenceClasses.add(movedEqvClass);
                numberMoved = sizeOfFEqvClass;
                
            } 
            else 
            {
                // shuffle the equivalence class prior to a partial selection
                if (m_shuffleEquivalenceClasses) 
                {
                    java.util.Collections.shuffle(firstEqvClass);
                }
                
                movedEqvClass = new ArrayList(firstEqvClass.subList(0, numberToRemove));
                firstEqvClass.subList(0, numberToRemove).clear();
                newSet.m_equivalenceClasses.add(movedEqvClass);
                numberMoved = numberToRemove;
            }
            
            m_size -= numberMoved;
            newSet.m_size += numberMoved;
            numberToRemove -= numberMoved;
            
            // now fix up the item to class map
            Iterator iter = movedEqvClass.iterator();
            while (iter.hasNext()) 
            {
                Object obj = iter.next();
                m_itemToClassMap.remove(obj);                
                newSet.m_itemToClassMap.put(obj, movedEqvClass);                
            }
        }
        
        if (newSet.size() > 0) 
        {
            newSet.resetLoopIterator();
            resetLoopIterator();
            m_changeID += 1;
            newSet.m_changeID += 1;
        }
        assert m_size == m_itemToClassMap.size(); 
        assert newSet.m_size == newSet.m_itemToClassMap.size(); 
        return newSet;
    }
    
    /**
     * In some cases, the equivalence class of an object will change.  This can leave
     * the list in an inconsistent state.  It is essential to fix the problem. 
     * This method moves the object to the correct equivalence class, keeping the 
     * loopIterator where it was.
     * 
     * @param arg0 The object to move.
     * @return true if the object is a member of the set, false otherwise.
     */
    public boolean resetEquivalenceClass(T arg0) 
    {
        boolean found = false;
        EqvPosition eqvPosition = new EqvPosition();
        eqvPosition.matchingEqvClass = (List)m_itemToClassMap.get(arg0);
        
        if (eqvPosition.matchingEqvClass != null) 
        {
            removeAtPosition(eqvPosition, arg0);
            found = true;
        }
        
        if (found) 
        {
            addExpired(arg0);
        }
        
        return found;
    }
}





If the given objects are equal

    
import java.lang.reflect.Array;
import java.util.Arrays;
/*
 * Copyright 2002-2007 the original author or authors.
 *
 * Licensed 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.
 */
//Revised from springframework
/**
 * Miscellaneous object utility methods. Mainly for internal use within the
 * framework; consider Jakarta"s Commons Lang for a more comprehensive suite
 * of object utilities.
 *
 * @author Juergen Hoeller
 * @author Keith Donald
 * @author Rod Johnson
 * @author Rob Harrop
 * @author Alex Ruiz
 * @since 19.03.2004
 * @see org.apache.rumons.lang.ObjectUtils
 */
abstract class ObjectUtils {
  private static final int INITIAL_HASH = 7;
  private static final int MULTIPLIER = 31;
  private static final String EMPTY_STRING = "";
  private static final String NULL_STRING = "null";
  private static final String ARRAY_START = "{";
  private static final String ARRAY_END = "}";
  private static final String EMPTY_ARRAY = ARRAY_START + ARRAY_END;
  private static final String ARRAY_ELEMENT_SEPARATOR = ", ";

  /**
   * Determine if the given objects are equal, returning <code>true</code>
   * if both are <code>null</code> or <code>false</code> if only one is
   * <code>null</code>.
   * <p>Compares arrays with <code>Arrays.equals</code>, performing an equality
   * check based on the array elements rather than the array reference.
   * @param o1 first Object to compare
   * @param o2 second Object to compare
   * @return whether the given objects are equal
   * @see java.util.Arrays#equals
   */
  public static boolean nullSafeEquals(Object o1, Object o2) {
    if (o1 == o2) {
      return true;
    }
    if (o1 == null || o2 == null) {
      return false;
    }
    if (o1.equals(o2)) {
      return true;
    }
    if (o1 instanceof Object[] && o2 instanceof Object[]) {
      return Arrays.equals((Object[]) o1, (Object[]) o2);
    }
    if (o1 instanceof boolean[] && o2 instanceof boolean[]) {
      return Arrays.equals((boolean[]) o1, (boolean[]) o2);
    }
    if (o1 instanceof byte[] && o2 instanceof byte[]) {
      return Arrays.equals((byte[]) o1, (byte[]) o2);
    }
    if (o1 instanceof char[] && o2 instanceof char[]) {
      return Arrays.equals((char[]) o1, (char[]) o2);
    }
    if (o1 instanceof double[] && o2 instanceof double[]) {
      return Arrays.equals((double[]) o1, (double[]) o2);
    }
    if (o1 instanceof float[] && o2 instanceof float[]) {
      return Arrays.equals((float[]) o1, (float[]) o2);
    }
    if (o1 instanceof int[] && o2 instanceof int[]) {
      return Arrays.equals((int[]) o1, (int[]) o2);
    }
    if (o1 instanceof long[] && o2 instanceof long[]) {
      return Arrays.equals((long[]) o1, (long[]) o2);
    }
    if (o1 instanceof short[] && o2 instanceof short[]) {
      return Arrays.equals((short[]) o1, (short[]) o2);
    }
    return false;
  }

}





Test the equality of two object arrays

 
import java.lang.reflect.Array;
/*
 * JBoss, Home of Professional Open Source
 * Copyright 2005, JBoss Inc., and individual contributors as indicated
 * by the @authors tag. See the copyright.txt in the distribution for a
 * full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
public class Main {

  /**
   * Test the equality of two object arrays.
   *
   * @param a       The first array.
   * @param b       The second array.
   * @param deep    True to traverse elements which are arrays.
   * @return        True if arrays are equal.
   */
  public static boolean equals(final Object[] a, final Object[] b,
                               final boolean deep)
  {
     if (a == b) return true;
     if (a == null || b == null) return false;
     if (a.length != b.length) return false;
     for (int i=0; i<a.length; i++) {
        Object x = a[i];
        Object y = b[i];
        if (x != y) return false;
        if (x == null || y == null) return false;
        if (deep) {
           if (x instanceof Object[] && y instanceof Object[]) {
              if (! equals((Object[])x, (Object[])y, true)) return false;
           }
           else {
              return false;
           }
        }
        if (! x.equals(y)) return false;
     }
     return true;
  }
  /**
   * Test the equality of two object arrays.
   *
   * @param a    The first array.
   * @param b    The second array.
   * @return     True if arrays are equal.
   */
  public static boolean equals(final Object[] a, final Object[] b) {
     return equals(a, b, true);
  }
}