Skip to content

Commit d760db1

Browse files
puredangerstuarthalloway
authored andcommitted
Direct iterators for PersistentHashMap, APersistentSet, PersistentQueue, and PersistentStructMap, and records. Added new IMapIterable interface for key and val iterators.
Signed-off-by: Stuart Halloway <[email protected]>
1 parent 3d216d4 commit d760db1

10 files changed

+446
-13
lines changed

src/clj/clojure/core_deftype.clj

+2-1
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,8 @@
215215
(clojure.lang.MapEntry. k# v#))))
216216
`(seq [this#] (seq (concat [~@(map #(list `new `clojure.lang.MapEntry (keyword %) %) base-fields)]
217217
~'__extmap)))
218-
`(iterator [this#] (clojure.lang.SeqIterator. (.seq this#)))
218+
`(iterator [~gs]
219+
(clojure.lang.RecordIterator. ~gs [~@(map keyword base-fields)] (RT/iter ~'__extmap)))
219220
`(assoc [this# k# ~gs]
220221
(condp identical? k#
221222
~@(mapcat (fn [fld]

src/jvm/clojure/lang/APersistentMap.java

+17
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,23 @@ public ValSeq withMeta(IPersistentMap meta){
194194
}
195195
}
196196

197+
static final IFn MAKE_ENTRY = new AFn() {
198+
public Object invoke(Object key, Object val) {
199+
return new MapEntry(key, val);
200+
}
201+
};
202+
203+
static final IFn MAKE_KEY = new AFn() {
204+
public Object invoke(Object key, Object val) {
205+
return key;
206+
}
207+
};
208+
209+
static final IFn MAKE_VAL = new AFn() {
210+
public Object invoke(Object key, Object val) {
211+
return val;
212+
}
213+
};
197214

198215
public Object invoke(Object arg1) {
199216
return valAt(arg1);

src/jvm/clojure/lang/APersistentSet.java

+17-1
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,23 @@ public boolean isEmpty(){
170170
}
171171

172172
public Iterator iterator(){
173-
return new SeqIterator(this);
173+
if(impl instanceof IMapIterable)
174+
return ((IMapIterable)impl).keyIterator();
175+
else return new Iterator() {
176+
private final Iterator iter = impl.iterator();
177+
178+
public boolean hasNext() {
179+
return iter.hasNext();
180+
}
181+
182+
public Object next() {
183+
return ((IMapEntry)iter.next()).key();
184+
}
185+
186+
public void remove() {
187+
throw new UnsupportedOperationException();
188+
}
189+
};
174190
}
175191

176192
}
+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* Copyright (c) Rich Hickey. All rights reserved.
3+
* The use and distribution terms for this software are covered by the
4+
* Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
5+
* which can be found in the file epl-v10.html at the root of this distribution.
6+
* By using this software in any fashion, you are agreeing to be bound by
7+
* the terms of this license.
8+
* You must not remove this notice, or any other, from this software.
9+
**/
10+
11+
/* Alex Miller Dec 3, 2014 */
12+
13+
package clojure.lang;
14+
15+
import java.util.Iterator;
16+
17+
/**
18+
* Indicate a map can provide more efficient key and val iterators.
19+
*/
20+
public interface IMapIterable {
21+
22+
Iterator keyIterator();
23+
24+
Iterator valIterator();
25+
26+
}

src/jvm/clojure/lang/PersistentArrayMap.java

+16-6
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
* null keys and values are ok, but you won't be able to distinguish a null value via valAt - use contains/entryAt
2727
*/
2828

29-
public class PersistentArrayMap extends APersistentMap implements IObj, IEditableCollection {
29+
public class PersistentArrayMap extends APersistentMap implements IObj, IEditableCollection, IMapIterable {
3030

3131
final Object[] array;
3232
static final int HASHTABLE_THRESHOLD = 16;
@@ -281,7 +281,15 @@ static boolean equalKey(Object k1, Object k2){
281281
}
282282

283283
public Iterator iterator(){
284-
return new Iter(array);
284+
return new Iter(array,APersistentMap.MAKE_ENTRY);
285+
}
286+
287+
public Iterator keyIterator(){
288+
return new Iter(array,APersistentMap.MAKE_KEY);
289+
}
290+
291+
public Iterator valIterator() {
292+
return new Iter(array,APersistentMap.MAKE_VAL);
285293
}
286294

287295
public ISeq seq(){
@@ -329,18 +337,20 @@ public Obj withMeta(IPersistentMap meta){
329337
}
330338

331339
static class Iter implements Iterator{
340+
IFn f;
332341
Object[] array;
333342
int i;
334343

335344
//for iterator
336-
Iter(Object[] array){
337-
this(array, -2);
345+
Iter(Object[] array, IFn f){
346+
this(array, -2, f);
338347
}
339348

340349
//for entryAt
341-
Iter(Object[] array, int i){
350+
Iter(Object[] array, int i, IFn f){
342351
this.array = array;
343352
this.i = i;
353+
this.f = f;
344354
}
345355

346356
public boolean hasNext(){
@@ -349,7 +359,7 @@ public boolean hasNext(){
349359

350360
public Object next(){
351361
i += 2;
352-
return new MapEntry(array[i],array[i+1]);
362+
return f.invoke(array[i],array[i+1]);
353363
}
354364

355365
public void remove(){

src/jvm/clojure/lang/PersistentHashMap.java

+178-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
Any errors are my own
2626
*/
2727

28-
public class PersistentHashMap extends APersistentMap implements IEditableCollection, IObj {
28+
public class PersistentHashMap extends APersistentMap implements IEditableCollection, IObj, IMapIterable {
2929

3030
final int count;
3131
final INode root;
@@ -173,8 +173,59 @@ public IPersistentMap without(Object key){
173173
return new PersistentHashMap(meta(), count - 1, newroot, hasNull, nullValue);
174174
}
175175

176+
static final Iterator EMPTY_ITER = new Iterator(){
177+
public boolean hasNext(){
178+
return false;
179+
}
180+
181+
public Object next(){
182+
throw new NoSuchElementException();
183+
}
184+
185+
public void remove(){
186+
throw new UnsupportedOperationException();
187+
}
188+
};
189+
190+
private Iterator iterator(final IFn f){
191+
final Iterator rootIter = (root == null) ? EMPTY_ITER : root.iterator(f);
192+
if(hasNull) {
193+
return new Iterator() {
194+
private boolean seen = false;
195+
public boolean hasNext() {
196+
if (!seen)
197+
return true;
198+
else
199+
return rootIter.hasNext();
200+
}
201+
202+
public Object next(){
203+
if (!seen) {
204+
seen = true;
205+
return f.invoke(null, nullValue);
206+
} else
207+
return rootIter.next();
208+
}
209+
210+
public void remove(){
211+
throw new UnsupportedOperationException();
212+
}
213+
};
214+
}
215+
else
216+
return rootIter;
217+
}
218+
176219
public Iterator iterator(){
177-
return new SeqIterator(this);
220+
return iterator(APersistentMap.MAKE_ENTRY);
221+
}
222+
223+
public Iterator keyIterator(){
224+
return iterator(APersistentMap.MAKE_KEY);
225+
}
226+
227+
public Iterator valIterator(){
228+
return iterator(APersistentMap.MAKE_VAL);
178229
}
179230

180231
public Object kvreduce(IFn f, Object init){
@@ -340,6 +391,9 @@ static interface INode extends Serializable {
340391
public Object kvreduce(IFn f, Object init);
341392

342393
Object fold(IFn combinef, IFn reducef, IFn fjtask, IFn fjfork, IFn fjjoin);
394+
395+
// returns the result of (f [k v]) for each iterated element
396+
Iterator iterator(IFn f);
343397
}
344398

345399
final static class ArrayNode implements INode{
@@ -400,6 +454,10 @@ public ISeq nodeSeq(){
400454
return Seq.create(array);
401455
}
402456

457+
public Iterator iterator(IFn f){
458+
return new Iter(array, f);
459+
}
460+
403461
public Object kvreduce(IFn f, Object init){
404462
for(INode node : array){
405463
if(node != null){
@@ -563,6 +621,49 @@ public ISeq next() {
563621
}
564622

565623
}
624+
625+
static class Iter implements Iterator {
626+
private final INode[] array;
627+
private final IFn f;
628+
private int i = 0;
629+
private Iterator nestedIter;
630+
631+
private Iter(INode[] array, IFn f){
632+
this.array = array;
633+
this.f = f;
634+
}
635+
636+
public boolean hasNext(){
637+
while(true)
638+
{
639+
if(nestedIter != null)
640+
if(nestedIter.hasNext())
641+
return true;
642+
else
643+
nestedIter = null;
644+
645+
if(i < array.length)
646+
{
647+
INode node = array[i++];
648+
if (node != null)
649+
nestedIter = node.iterator(f);
650+
}
651+
else
652+
return false;
653+
}
654+
}
655+
656+
public Object next(){
657+
if(hasNext())
658+
return nestedIter.next();
659+
else
660+
throw new NoSuchElementException();
661+
}
662+
663+
public void remove(){
664+
throw new UnsupportedOperationException();
665+
}
666+
}
566667
}
567668

568669
final static class BitmapIndexedNode implements INode{
@@ -687,6 +788,10 @@ public ISeq nodeSeq(){
687788
return NodeSeq.create(array);
688789
}
689790

791+
public Iterator iterator(IFn f){
792+
return new NodeIter(array, f);
793+
}
794+
690795
public Object kvreduce(IFn f, Object init){
691796
return NodeSeq.kvreduce(array,f,init);
692797
}
@@ -879,6 +984,10 @@ public ISeq nodeSeq(){
879984
return NodeSeq.create(array);
880985
}
881986

987+
public Iterator iterator(IFn f){
988+
return new NodeIter(array, f);
989+
}
990+
882991
public Object kvreduce(IFn f, Object init){
883992
return NodeSeq.kvreduce(array,f,init);
884993
}
@@ -1107,6 +1216,73 @@ private static int bitpos(int hash, int shift){
11071216
return 1 << mask(hash, shift);
11081217
}
11091218

1219+
static final class NodeIter implements Iterator {
1220+
private static final Object NULL = new Object();
1221+
final Object[] array;
1222+
final IFn f;
1223+
private int i = 0;
1224+
private Object nextEntry = NULL;
1225+
private Iterator nextIter;
1226+
1227+
NodeIter(Object[] array, IFn f){
1228+
this.array = array;
1229+
this.f = f;
1230+
}
1231+
1232+
private boolean advance(){
1233+
while (i<array.length)
1234+
{
1235+
Object key = array[i];
1236+
Object nodeOrVal = array[i+1];
1237+
i += 2;
1238+
if (key != null)
1239+
{
1240+
nextEntry = f.invoke(key, nodeOrVal);
1241+
return true;
1242+
}
1243+
else if(nodeOrVal != null)
1244+
{
1245+
Iterator iter = ((INode) nodeOrVal).iterator(f);
1246+
if(iter != null && iter.hasNext())
1247+
{
1248+
nextIter = iter;
1249+
return true;
1250+
}
1251+
}
1252+
}
1253+
return false;
1254+
}
1255+
1256+
public boolean hasNext(){
1257+
if (nextEntry != NULL || nextIter != null)
1258+
return true;
1259+
return advance();
1260+
}
1261+
1262+
public Object next(){
1263+
Object ret = nextEntry;
1264+
if(ret != NULL)
1265+
{
1266+
nextEntry = NULL;
1267+
return ret;
1268+
}
1269+
else if(nextIter != null)
1270+
{
1271+
ret = nextIter.next();
1272+
if(! nextIter.hasNext())
1273+
nextIter = null;
1274+
return ret;
1275+
}
1276+
else if(advance())
1277+
return next();
1278+
throw new NoSuchElementException();
1279+
}
1280+
1281+
public void remove(){
1282+
throw new UnsupportedOperationException();
1283+
}
1284+
}
1285+
11101286
static final class NodeSeq extends ASeq {
11111287
final Object[] array;
11121288
final int i;

0 commit comments

Comments
 (0)