diff options
author | Rich Hickey <richhickey@gmail.com> | 2009-01-15 15:50:30 +0000 |
---|---|---|
committer | Rich Hickey <richhickey@gmail.com> | 2009-01-15 15:50:30 +0000 |
commit | 8f0a24409ce9e905222b03377175533e1cab3be6 (patch) | |
tree | bf96e3b9845171ee8234ed76ee78441d5df80fd0 | |
parent | 9fcd8aea5940019c7d5ee25ddaf4b43f6339a326 (diff) |
hashCode/equals/= cleanup, maximize alignment with j.u.Collections
seqs/lists now implement List
-rw-r--r-- | src/clj/clojure/core.clj | 12 | ||||
-rw-r--r-- | src/jvm/clojure/lang/APersistentMap.java | 23 | ||||
-rw-r--r-- | src/jvm/clojure/lang/APersistentSet.java | 4 | ||||
-rw-r--r-- | src/jvm/clojure/lang/APersistentVector.java | 57 | ||||
-rw-r--r-- | src/jvm/clojure/lang/ASeq.java | 87 | ||||
-rw-r--r-- | src/jvm/clojure/lang/Compiler.java | 6 | ||||
-rw-r--r-- | src/jvm/clojure/lang/IPersistentCollection.java | 2 | ||||
-rw-r--r-- | src/jvm/clojure/lang/PersistentHashMap.java | 6 | ||||
-rw-r--r-- | src/jvm/clojure/lang/PersistentList.java | 67 | ||||
-rw-r--r-- | src/jvm/clojure/lang/PersistentQueue.java | 18 | ||||
-rw-r--r-- | src/jvm/clojure/lang/RT.java | 6 | ||||
-rw-r--r-- | src/jvm/clojure/lang/Util.java | 10 |
12 files changed, 257 insertions, 41 deletions
diff --git a/src/clj/clojure/core.clj b/src/clj/clojure/core.clj index e0ebaf65..d21c3f02 100644 --- a/src/clj/clojure/core.clj +++ b/src/clj/clojure/core.clj @@ -458,14 +458,14 @@ (defn = "Equality. Returns true if x equals y, false if not. Same as Java x.equals(y) except it also works for nil, and compares - numbers in a type-independent manner. Clojure's immutable data + numbers and collections in a type-independent manner. Clojure's immutable data structures define equals() (and thus =) as a value, not an identity, comparison." {:tag Boolean - :inline (fn [x y] `(. clojure.lang.Util equal ~x ~y)) + :inline (fn [x y] `(. clojure.lang.Util equiv ~x ~y)) :inline-arities #{2}} ([x] true) - ([x y] (. clojure.lang.Util (equal x y))) + ([x y] (clojure.lang.Util/equiv x y)) ([x y & more] (if (= x y) (if (rest more) @@ -486,8 +486,8 @@ (defn compare "Comparator. Returns 0 if x equals y, -1 if x is logically 'less than' y, else 1. Same as Java x.compareTo(y) except it also works - for nil, and compares numbers in a type-independent manner. x must - implement Comparable" + for nil, and compares numbers and collections in a type-independent + manner. x must implement Comparable" {:tag Integer :inline (fn [x y] `(. clojure.lang.Util compare ~x ~y))} [x y] (. clojure.lang.Util (compare x y))) @@ -3237,7 +3237,7 @@ (defn distinct? - "Returns true if no two of the arguments are equal" + "Returns true if no two of the arguments are =" {:tag Boolean} ([x] true) ([x y] (not (= x y))) diff --git a/src/jvm/clojure/lang/APersistentMap.java b/src/jvm/clojure/lang/APersistentMap.java index c90538ae..4d8bc8f7 100644 --- a/src/jvm/clojure/lang/APersistentMap.java +++ b/src/jvm/clojure/lang/APersistentMap.java @@ -65,13 +65,32 @@ public boolean equals(Object obj){ Map.Entry e = (Map.Entry) s.first();
boolean found = m.containsKey(e.getKey());
- if(!found || !Util.equal(e.getValue(), m.get(e.getKey())))
+ if(!found || !Util.equals(e.getValue(), m.get(e.getKey())))
return false;
}
return true;
}
+public boolean equiv(Object obj){
+ if(!(obj instanceof Map))
+ return false;
+ Map m = (Map) obj;
+
+ if(m.size() != size())
+ return false;
+
+ for(ISeq s = seq(); s != null; s = s.rest())
+ {
+ Map.Entry e = (Map.Entry) s.first();
+ boolean found = m.containsKey(e.getKey());
+
+ if(!found || !Util.equiv(e.getValue(), m.get(e.getKey())))
+ return false;
+ }
+
+ return true;
+}
public int hashCode(){
if(_hash == -1)
{
@@ -190,7 +209,7 @@ public Set entrySet(){ {
Entry e = (Entry) o;
Entry found = entryAt(e.getKey());
- if(found != null && Util.equal(found.getValue(), e.getValue()))
+ if(found != null && Util.equals(found.getValue(), e.getValue()))
return true;
}
return false;
diff --git a/src/jvm/clojure/lang/APersistentSet.java b/src/jvm/clojure/lang/APersistentSet.java index b48b377c..36063276 100644 --- a/src/jvm/clojure/lang/APersistentSet.java +++ b/src/jvm/clojure/lang/APersistentSet.java @@ -71,6 +71,10 @@ public boolean equals(Object obj){ return true; } +public boolean equiv(Object o){ + return equals(o); +} + public int hashCode(){ if(_hash == -1) { diff --git a/src/jvm/clojure/lang/APersistentVector.java b/src/jvm/clojure/lang/APersistentVector.java index 0ecf9263..fe891ef7 100644 --- a/src/jvm/clojure/lang/APersistentVector.java +++ b/src/jvm/clojure/lang/APersistentVector.java @@ -52,7 +52,7 @@ static boolean doEquals(IPersistentVector v, Object obj){ for(Iterator i1 = ((List) v).iterator(), i2 = ma.iterator(); i1.hasNext();) { - if(!Util.equal(i1.next(), i2.next())) + if(!Util.equals(i1.next(), i2.next())) return false; } return true; @@ -75,7 +75,50 @@ static boolean doEquals(IPersistentVector v, Object obj){ ISeq ms = ((IPersistentCollection) obj).seq(); for(int i = 0; i < v.count(); i++, ms = ms.rest()) { - if(ms == null || !Util.equal(v.nth(i), ms.first())) + if(ms == null || !Util.equals(v.nth(i), ms.first())) + return false; + } + if(ms != null) + return false; + } + + return true; + +} + +static boolean doEquiv(IPersistentVector v, Object obj){ + if(obj instanceof List || obj instanceof IPersistentVector) + { + Collection ma = (Collection) obj; + if(ma.size() != v.count()) + return false; + for(Iterator i1 = ((List) v).iterator(), i2 = ma.iterator(); + i1.hasNext();) + { + if(!Util.equiv(i1.next(), i2.next())) + return false; + } + return true; + } +// if(obj instanceof IPersistentVector) +// { +// IPersistentVector ma = (IPersistentVector) obj; +// if(ma.count() != v.count() || ma.hashCode() != v.hashCode()) +// return false; +// for(int i = 0; i < v.count(); i++) +// { +// if(!Util.equal(v.nth(i), ma.nth(i))) +// return false; +// } +// } + else + { + if(!(obj instanceof Sequential)) + return false; + ISeq ms = ((IPersistentCollection) obj).seq(); + for(int i = 0; i < v.count(); i++, ms = ms.rest()) + { + if(ms == null || !Util.equiv(v.nth(i), ms.first())) return false; } if(ms != null) @@ -90,6 +133,10 @@ public boolean equals(Object obj){ return doEquals(this, obj); } +public boolean equiv(Object obj){ + return doEquiv(this, obj); +} + public int hashCode(){ if(_hash == -1) { @@ -120,14 +167,14 @@ public Object remove(int i){ public int indexOf(Object o){ for(int i = 0; i < count(); i++) - if(Util.equal(nth(i), o)) + if(Util.equiv(nth(i), o)) return i; return -1; } public int lastIndexOf(Object o){ for(int i = count() - 1; i >= 0; i--) - if(Util.equal(nth(i), o)) + if(Util.equiv(nth(i), o)) return i; return -1; } @@ -333,7 +380,7 @@ public boolean isEmpty(){ public boolean contains(Object o){ for(ISeq s = seq(); s != null; s = s.rest()) { - if(Util.equal(s.first(), o)) + if(Util.equiv(s.first(), o)) return true; } return false; diff --git a/src/jvm/clojure/lang/ASeq.java b/src/jvm/clojure/lang/ASeq.java index 9e298301..8bd830d1 100644 --- a/src/jvm/clojure/lang/ASeq.java +++ b/src/jvm/clojure/lang/ASeq.java @@ -10,11 +10,9 @@ package clojure.lang;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.concurrent.atomic.AtomicReference;
+import java.util.*;
-public abstract class ASeq extends Obj implements ISeq, Collection, Streamable{
+public abstract class ASeq extends Obj implements ISeq, List, Streamable{
transient int _hash = -1;
public String toString(){
@@ -33,20 +31,32 @@ protected ASeq(IPersistentMap meta){ protected ASeq(){
}
-public boolean equals(Object obj){
+public boolean equiv(Object obj){
- if(!(obj instanceof Sequential))
+ if(!(obj instanceof Sequential || obj instanceof List))
return false;
- ISeq ms = ((IPersistentCollection) obj).seq();
+ ISeq ms = RT.seq(obj);
for(ISeq s = seq(); s != null; s = s.rest(), ms = ms.rest())
{
- if(ms == null || !Util.equal(s.first(), ms.first()))
+ if(ms == null || !Util.equiv(s.first(), ms.first()))
return false;
}
- if(ms != null)
+ return ms == null;
+
+}
+
+public boolean equals(Object obj){
+
+ if(!(obj instanceof Sequential || obj instanceof List))
return false;
+ ISeq ms = RT.seq(obj);
+ for(ISeq s = seq(); s != null; s = s.rest(), ms = ms.rest())
+ {
+ if(ms == null || !Util.equals(s.first(), ms.first()))
+ return false;
+ }
+ return ms == null;
- return true;
}
public int hashCode(){
@@ -166,7 +176,7 @@ public boolean isEmpty(){ public boolean contains(Object o){
for(ISeq s = seq(); s != null; s = s.rest())
{
- if(Util.equal(s.first(), o))
+ if(Util.equiv(s.first(), o))
return true;
}
return false;
@@ -177,6 +187,8 @@ public Iterator iterator(){ return new SeqIterator(this);
}
+
+
public IStream stream() throws Exception {
return new Stream(this);
}
@@ -198,4 +210,57 @@ public IStream stream() throws Exception { return RT.eos();
}
}
+
+
+//////////// List stuff /////////////////
+private List reify(){
+ return new ArrayList(this);
+}
+
+public List subList(int fromIndex, int toIndex){
+ return reify().subList(fromIndex, toIndex);
+}
+
+public Object set(int index, Object element){
+ throw new UnsupportedOperationException();
+}
+
+public Object remove(int index){
+ throw new UnsupportedOperationException();
+}
+
+public int indexOf(Object o){
+ ISeq s = seq();
+ for(int i = 0; s != null; s = s.rest(), i++)
+ {
+ if(Util.equiv(s.first(), o))
+ return i;
+ }
+ return -1;
+}
+
+public int lastIndexOf(Object o){
+ return reify().lastIndexOf(o);
+}
+
+public ListIterator listIterator(){
+ return reify().listIterator();
+}
+
+public ListIterator listIterator(int index){
+ return reify().listIterator(index);
+}
+
+public Object get(int index){
+ return RT.nth(this, index);
+}
+
+public void add(int index, Object element){
+ throw new UnsupportedOperationException();
+}
+
+public boolean addAll(int index, Collection c){
+ throw new UnsupportedOperationException();
+}
+
}
diff --git a/src/jvm/clojure/lang/Compiler.java b/src/jvm/clojure/lang/Compiler.java index 5fc6f6fc..cee50024 100644 --- a/src/jvm/clojure/lang/Compiler.java +++ b/src/jvm/clojure/lang/Compiler.java @@ -1808,7 +1808,7 @@ public static class TryExpr implements Expr{ { Object f = fs.first(); Object op = (f instanceof ISeq) ? ((ISeq) f).first() : null; - if(!Util.equal(op, CATCH) && !Util.equal(op, FINALLY)) + if(!Util.equals(op, CATCH) && !Util.equals(op, FINALLY)) { if(caught) throw new Exception("Only catch or finally clause can follow catch in try expression"); @@ -1816,7 +1816,7 @@ public static class TryExpr implements Expr{ } else { - if(Util.equal(op, CATCH)) + if(Util.equals(op, CATCH)) { Class c = HostExpr.maybeClass(RT.second(f), false); if(c == null) @@ -3627,7 +3627,7 @@ public static class BodyExpr implements Expr{ static class Parser implements IParser{ public Expr parse(C context, Object frms) throws Exception{ ISeq forms = (ISeq) frms; - if(Util.equal(RT.first(forms), DO)) + if(Util.equals(RT.first(forms), DO)) forms = RT.rest(forms); PersistentVector exprs = PersistentVector.EMPTY; for(; forms != null; forms = forms.rest()) diff --git a/src/jvm/clojure/lang/IPersistentCollection.java b/src/jvm/clojure/lang/IPersistentCollection.java index 21e436f2..a79534ae 100644 --- a/src/jvm/clojure/lang/IPersistentCollection.java +++ b/src/jvm/clojure/lang/IPersistentCollection.java @@ -20,4 +20,6 @@ ISeq seq(); IPersistentCollection cons(Object o);
IPersistentCollection empty();
+
+boolean equiv(Object o);
}
diff --git a/src/jvm/clojure/lang/PersistentHashMap.java b/src/jvm/clojure/lang/PersistentHashMap.java index 014dcefb..d696cd5a 100644 --- a/src/jvm/clojure/lang/PersistentHashMap.java +++ b/src/jvm/clojure/lang/PersistentHashMap.java @@ -536,7 +536,7 @@ final static class LeafNode extends AMapEntry implements INode{ public INode assoc(int shift, int hash, Object key, Object val, Box addedLeaf){ if(hash == this.hash) { - if(Util.equal(key, this.key)) + if(Util.equals(key, this.key)) { if(val == this.val) return this; @@ -552,13 +552,13 @@ final static class LeafNode extends AMapEntry implements INode{ } public INode without(int hash, Object key){ - if(hash == this.hash && Util.equal(key, this.key)) + if(hash == this.hash && Util.equals(key, this.key)) return null; return this; } public LeafNode find(int hash, Object key){ - if(hash == this.hash && Util.equal(key, this.key)) + if(hash == this.hash && Util.equals(key, this.key)) return this; return null; } diff --git a/src/jvm/clojure/lang/PersistentList.java b/src/jvm/clojure/lang/PersistentList.java index 13a77f7e..b3949007 100644 --- a/src/jvm/clojure/lang/PersistentList.java +++ b/src/jvm/clojure/lang/PersistentList.java @@ -12,7 +12,7 @@ package clojure.lang; import java.util.*;
-public class PersistentList extends ASeq implements IPersistentList, IReduce{
+public class PersistentList extends ASeq implements IPersistentList, IReduce, List{
private final Object _first;
private final IPersistentList _rest;
@@ -112,18 +112,22 @@ public Object reduce(IFn f, Object start) throws Exception{ return ret;
}
-static class EmptyList extends Obj implements IPersistentList, Collection{
- @Override
+
+static class EmptyList extends Obj implements IPersistentList, List{
+
public int hashCode(){
return 1;
}
- @Override
public boolean equals(Object o) {
- return o instanceof Sequential && RT.count(o) == 0;
+ return (o instanceof Sequential || o instanceof List) && RT.count(o) == 0;
}
+ public boolean equiv(Object o){
+ return equals(o);
+ }
+
EmptyList(IPersistentMap meta){
super(meta);
}
@@ -225,6 +229,59 @@ static class EmptyList extends Obj implements IPersistentList, Collection{ objects[0] = null;
return objects;
}
+
+ //////////// List stuff /////////////////
+ private List reify(){
+ return new ArrayList(this);
+ }
+
+ public List subList(int fromIndex, int toIndex){
+ return reify().subList(fromIndex, toIndex);
+ }
+
+ public Object set(int index, Object element){
+ throw new UnsupportedOperationException();
+ }
+
+ public Object remove(int index){
+ throw new UnsupportedOperationException();
+ }
+
+ public int indexOf(Object o){
+ ISeq s = seq();
+ for(int i = 0; s != null; s = s.rest(), i++)
+ {
+ if(Util.equiv(s.first(), o))
+ return i;
+ }
+ return -1;
+ }
+
+ public int lastIndexOf(Object o){
+ return reify().lastIndexOf(o);
+ }
+
+ public ListIterator listIterator(){
+ return reify().listIterator();
+ }
+
+ public ListIterator listIterator(int index){
+ return reify().listIterator(index);
+ }
+
+ public Object get(int index){
+ return RT.nth(this, index);
+ }
+
+ public void add(int index, Object element){
+ throw new UnsupportedOperationException();
+ }
+
+ public boolean addAll(int index, Collection c){
+ throw new UnsupportedOperationException();
+ }
+
+
}
}
diff --git a/src/jvm/clojure/lang/PersistentQueue.java b/src/jvm/clojure/lang/PersistentQueue.java index 8fbeb281..0d2ec9a9 100644 --- a/src/jvm/clojure/lang/PersistentQueue.java +++ b/src/jvm/clojure/lang/PersistentQueue.java @@ -37,6 +37,20 @@ PersistentQueue(IPersistentMap meta, ISeq f, PersistentVector r){ this.r = r;
}
+public boolean equiv(Object obj){
+
+ if(!(obj instanceof Sequential))
+ return false;
+ ISeq ms = ((IPersistentCollection) obj).seq();
+ for(ISeq s = seq(); s != null; s = s.rest(), ms = ms.rest())
+ {
+ if(ms == null || !Util.equiv(s.first(), ms.first()))
+ return false;
+ }
+ return ms.rest() == null;
+
+}
+
public boolean equals(Object obj){
if(!(obj instanceof Sequential))
@@ -44,7 +58,7 @@ public boolean equals(Object obj){ ISeq ms = ((IPersistentCollection) obj).seq();
for(ISeq s = seq(); s != null; s = s.rest(), ms = ms.rest())
{
- if(ms == null || !Util.equal(s.first(), ms.first()))
+ if(ms == null || !Util.equals(s.first(), ms.first()))
return false;
}
return ms.rest() == null;
@@ -214,7 +228,7 @@ public boolean isEmpty(){ public boolean contains(Object o){
for(ISeq s = seq(); s != null; s = s.rest())
{
- if(Util.equal(s.first(), o))
+ if(Util.equiv(s.first(), o))
return true;
}
return false;
diff --git a/src/jvm/clojure/lang/RT.java b/src/jvm/clojure/lang/RT.java index 4954028c..f5d97e83 100644 --- a/src/jvm/clojure/lang/RT.java +++ b/src/jvm/clojure/lang/RT.java @@ -705,7 +705,7 @@ static public Object nth(Object coll, int n){ return Character.valueOf(((String) coll).charAt(n)); else if(coll.getClass().isArray()) return Reflector.prepRet(Array.get(coll, n)); - else if(coll instanceof List) + else if(coll instanceof RandomAccess) return ((List) coll).get(n); else if(coll instanceof Matcher) return ((Matcher) coll).group(n); @@ -815,7 +815,7 @@ static public Object assocN(int n, Object val, Object coll){ } static boolean hasTag(Object o, Object tag){ - return Util.equal(tag, RT.get(RT.meta(o), TAG_KEY)); + return Util.equals(tag, RT.get(RT.meta(o), TAG_KEY)); } /** @@ -1410,7 +1410,7 @@ static public Object format(Object o, String s, Object... args) throws Exception Writer w; if(o == null) w = new StringWriter(); - else if(Util.equal(o, T)) + else if(Util.equals(o, T)) w = (Writer) OUT.get(); else w = (Writer) o; diff --git a/src/jvm/clojure/lang/Util.java b/src/jvm/clojure/lang/Util.java index b671e005..0fd54475 100644 --- a/src/jvm/clojure/lang/Util.java +++ b/src/jvm/clojure/lang/Util.java @@ -15,18 +15,26 @@ package clojure.lang; import java.math.BigInteger; public class Util{ -static public boolean equal(Object k1, Object k2){ +static public boolean equiv(Object k1, Object k2){ if(k1 == k2) return true; if(k1 != null) { if(k1 instanceof Number) return Numbers.equiv(k1, k2); + else if(k1 instanceof IPersistentCollection && k2 instanceof IPersistentCollection) + return ((IPersistentCollection)k1).equiv(k2); return k1.equals(k2); } return false; } +static public boolean equals(Object k1, Object k2){ + if(k1 == k2) + return true; + return k1 != null && k1.equals(k2); +} + static public int compare(Object k1, Object k2){ if(k1 == k2) return 0; |