patternjavaMinor
Pythonish integer range in Java - follow-up
Viewed 0 times
pythonishrangejavafollowinteger
Problem
(See the previous iteration.)
I have refactored the
Range.java:
```
package net.coderodde.util;
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
/**
* This class implements an iterator over an arithmetic progression of integers.
*
* @author Rodion "rodde" Efremov
* @version 1.6 (Dec 1, 2015)
*/
public class Range extends AbstractCollection
implements Iterable {
private final int start;
private final int step;
private final int stop;
public static Range range(int start, int stop, int step) {
return new Range(start, stop, step);
}
public static Range range(int start, int stop) {
return new Range(start, stop, 1);
}
public static Range range(int end) {
return new Range(0, end, 1);
}
private Range(int start, int stop, int step) {
if (step == 0) {
throw new IllegalArgumentException(
"Step argument must not be zero.");
}
this.start = start;
this.step = step;
this.stop = stop;
}
@Override
public Iterator iterator() {
return new RangeIterator();
}
@Override
public int size() {
return Math.max(0, step > 0 ? (stop + step - 1 - start) / step
: (stop + step + 1 - start) / step);
}
@Override
public boolean isEmpty() {
return size() == 0;
}
@Override
public boolean contains(Object o) {
try {
int n = (int)o;
boolean inBounds = step > 0 ? (start = n) && (n > stop);
return inBounds && (n - start) % step == 0;
} catch (ClassCastException oIsNotAnInt) {
return false;
}
}
public boolean remove(Object o) {
throw new UnsupportedOperationException(
"Removin
I have refactored the
Range; now it looks like this:Range.java:
```
package net.coderodde.util;
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
/**
* This class implements an iterator over an arithmetic progression of integers.
*
* @author Rodion "rodde" Efremov
* @version 1.6 (Dec 1, 2015)
*/
public class Range extends AbstractCollection
implements Iterable {
private final int start;
private final int step;
private final int stop;
public static Range range(int start, int stop, int step) {
return new Range(start, stop, step);
}
public static Range range(int start, int stop) {
return new Range(start, stop, 1);
}
public static Range range(int end) {
return new Range(0, end, 1);
}
private Range(int start, int stop, int step) {
if (step == 0) {
throw new IllegalArgumentException(
"Step argument must not be zero.");
}
this.start = start;
this.step = step;
this.stop = stop;
}
@Override
public Iterator iterator() {
return new RangeIterator();
}
@Override
public int size() {
return Math.max(0, step > 0 ? (stop + step - 1 - start) / step
: (stop + step + 1 - start) / step);
}
@Override
public boolean isEmpty() {
return size() == 0;
}
@Override
public boolean contains(Object o) {
try {
int n = (int)o;
boolean inBounds = step > 0 ? (start = n) && (n > stop);
return inBounds && (n - start) % step == 0;
} catch (ClassCastException oIsNotAnInt) {
return false;
}
}
public boolean remove(Object o) {
throw new UnsupportedOperationException(
"Removin
Solution
Builder Method Names
It's probably a matter of taste, but
Constants
It's very nice that the three
There should be a
contains(Object)
What will happen if it was called with
However, there is an alternative for the
Logic
According to the tests and the implementation, ranges like (1, 10, -1), (10, 1, 1) are considered as empty. This choice looks a bit weird, because these ranges have valid bounds, but the value of the
And what about ranges like (1, 10, 20), (10, 1, -20) ? Are they also empty?
If the current implementation does not correspond to a strict requirement, I'd suggest to define an empty range as a range where the
P.S. please remove the
It's probably a matter of taste, but
Range.range(1, 10); and the other two overloaded calls looks a bit redundant. It would look better as Range.of(1, 10);, wouldn't it?Constants
It's very nice that the three
int fields are final, but size() method performs calculations on each invocation of the method (plus if isEmpty() is called!), to produce the same result repetitively.There should be a
final int size field, calculated in the constructor (I wouldn't recommend to use lazy patterns here).contains(Object)
What will happen if it was called with
null arg? A NullPointerException. Since the ClassCastException is caught, I'd vote to add the NullPointer there also.However, there is an alternative for the
try-catch block in this case:public boolean contains(int n) {
boolean inBounds = step > 0 ? (start = n) && (n > stop);
return inBounds && (n - start) % step == 0;
}
@Override
public boolean contains(Object o) {
return false;
}Logic
According to the tests and the implementation, ranges like (1, 10, -1), (10, 1, 1) are considered as empty. This choice looks a bit weird, because these ranges have valid bounds, but the value of the
step field is inconsistent with these bounds, making them senseless. I think that the constructor arguments should be validated, with an IllegalArgumentException thrown for such cases.And what about ranges like (1, 10, 20), (10, 1, -20) ? Are they also empty?
If the current implementation does not correspond to a strict requirement, I'd suggest to define an empty range as a range where the
start and the stop values are equal.P.S. please remove the
main(args) method from Range class. There is already RangeTest for that.Code Snippets
public boolean contains(int n) {
boolean inBounds = step > 0 ? (start <= n) && (n < stop)
: (start >= n) && (n > stop);
return inBounds && (n - start) % step == 0;
}
@Override
public boolean contains(Object o) {
return false;
}Context
StackExchange Code Review Q#112553, answer score: 4
Revisions (0)
No revisions yet.