GROOVY-7877: The Range abstraction could support numeric ranges where…#366
GROOVY-7877: The Range abstraction could support numeric ranges where…#366paulk-asert wants to merge 1 commit intoapache:masterfrom
Conversation
|
A spike showing an alternative to PR#360. Basically it takes some of the optimizations desired for ObjectRange from that PR but only implements them in a new class NumberRange. It does also change the default class for creating ranges involving two (non-Integer) Numbers to use NumberRange instead of ObjectRange. The main thing which is supported is fast calculation of range items rather than relying on next() calls and ranges with a non-integral step size like this: It is a breaking change in that some weird edge cases involving metaprogramming on Numbers will behave differently. There is a slightly better backwards compatibility story than PR#360 however since users are still free to create ObjectRange's manually if they need the old behavior. I call it a spike because I still have some questions on the current design. For instance, with ObjectRange we have the following behavior: But currently inclusivity is remembered and applied to the 'to' end of the range when going in different directions, e.g. This may seem unintuitive at first but is kind of useful once understood. It is also somewhat essential to creating efficient ranges that can be traversed in either direction. But it's not the only possible design. We could brute force calculate the final to value (going in the forward direction) and use that as the from value when iterating in reverse. Another question as brought up by @tkruse in an earlier PR was whether for inaccurate number types like float whether it is better to continually add the step values when iterating or multiply by an index - giving at most two rounding errors rather than a compounding scenario. I also haven't tried to fast calculate all possible cases - it might be possible to cover additional cases. But that can be done I believe as a post optimization. It might also be possible to precalculate the number of items within the range during iteration which might save the cost of comparisons for each iteration. Also, there are some other optimizations possible. The expression |
| } | ||
| } | ||
|
|
||
| public boolean equals(Object that) { |
0c2f659 to
46e63e2
Compare
| final Iterator<Comparable> innerIterator = new StepIterator(this, stepSize); | ||
| return new Iterator<Comparable>() { | ||
| @Override | ||
| public synchronized boolean hasNext() { |
There was a problem hiding this comment.
Just curious, why synchronized for the iterator? I see the same on the ObjectRange iterator but can't understand why it would be needed.
There was a problem hiding this comment.
It isn't strictly necessary. It is providing a thread-safe iteration mechanism. This is something that was introduced recently in ObjectRange and I just carried it over.
There was a problem hiding this comment.
I figured it was carried over from ObjectRange. I just couldn't figure out why you'd want 2 or more threads to share an iterator. Figured the typical use would be in a foreach loop and each thread would get their own instance of an iterator. Otherwise, if sharing an instance a call to hasNext() followed by next() seems like it could return null for one of the threads after another thread got the last Number.
There was a problem hiding this comment.
We could possibly just have a safeIterator method which returned the thread-safe version and not bother normally.
There was a problem hiding this comment.
To me it seems like a true thread-safe iterator would need to lock in hasNext() and unlock when it returns false (or throws) or after a subsequent call by the same lock-holding thread to next(). Though I haven't test it, it appears with these synchronized methods that a thread could have hasNext() return true and a subsequent call to next() return null (though maybe should be NoSuchElementException).
But I still do not understand the benefit of having a thread safe version of the iterator that multiple threads can share and wonder if maybe those synchronized methods were put in by mistake (especially given the nearby comments about it not being thread safe). Also, I don't see similar synchronization in the IntRange class.
There was a problem hiding this comment.
and wonder if maybe those synchronized methods were put in by mistake (especially given the nearby comments about it not being thread safe).
Meant to say put in by mistake on the ObjectRange class originally.
| final Iterator it = new StepIterator(this, stepSize); | ||
| if (value == null) { | ||
| return false; | ||
| } |
There was a problem hiding this comment.
Minor thing, but should the null check come first.
|
I removed the slightly flawed attempts at synchronization and adopted a more Java behavior when calling next by throwing a NoSuchElementException once a NumberRange iterator is exhausted. |
|
Looks really good, nice work. Just my opinion, but I would not introduce the additional public API alternatives for equals/hashCode until a need presents itself. I think the doc in there stating instances of the class are not good candidates for hashed keys is good and sufficient. |
|
Forgot to ask if it would be worthwhile to add |
|
Yes, those additional methods do kind of represent possible premature feature introduction. I'll remove them (or at least comment them out) and I'll add the @SInCE tags too. |
e7bb103 to
327afd2
Compare
… the items in the range differ by some step size different to 1 (closes apache#366)
… the items in the range differ by some step size different to 1 (closes apache#366)
327afd2 to
d5d6a57
Compare
|
Proposed PR merged. |
… the items in the range differ by some step size different to 1 (closes apache#366)
… the items in the range differ by some step size different to 1