73

Let's say an integer should be within the range: [0...2147483647]

I want to check whether an integer variable falls within this range. I know it can be accomplished by a simple if-else statement, but is there a more efficient way to check whether it's within the range?

I'd rather not do this:

if (foo >= 0 && foo <= 2147483647) 
{
    // do something
}
3
  • If you’re asking for runtime efficiency (performance), I’m afraid that your if statement wins. I agree, though, that this observation shouldn’t drive your decision. For 99 % of applications, maintenance efficiency will be more important. Commented Jul 9, 2018 at 8:13
  • 3
    @OleV.V. I’d even simplify it to if(foo >= 0) …, as there’s no need to check whether an int is <=Integer.MAX_VALUE at all. But I doubt that the questioner will ever read these comments, as after five years of absence, coming back is quiet unlikely. Commented Jul 9, 2018 at 9:38
  • @OleV.V., Care has to be taken in how often and in what way this is called. Pipelining becomes 100% squashed on conditionals. If you can enforce a clamped range through some other "solid state" mathematical wizardry, then you'll be better off. Commented Dec 31, 2021 at 20:48

10 Answers 10

71

Apache Commons Lang has a Range class for doing arbitrary ranges.

Range<Integer> test = Range.between(1, 3);
System.out.println(test.contains(2));
System.out.println(test.contains(4));

Guava Range has similar API.

If you are just wanting to check if a number fits into a long value or an int value, you could try using it through BigDecimal. There are methods for longValueExact and intValueExact that throw exceptions if the value is too big for those precisions.

Sign up to request clarification or add additional context in comments.

4 Comments

Yes, but how do you think .contains(...) is implemented? ;) (With an if/else for sure :)
Do you know off hand if it is able to handle the outside-edges. (int example) something like <=-5 OR something like >= 12 ?
Answer my own question. "Yes" is the answer. See : Range.atMost and Range.atLeast. guava.dev/releases/snapshot/api/docs/com/google/common/collect/…
The problem with this Range is that it seems to be missing something akin to an addWrap() and subtractWrap() concepts, both of which are very useful when you need to dec/subtracting downward a value below minimum to wrap to max and similar for inc/adding a value to wrap when past max. You could even use the % if required and allow floating point. These are very easy to implement on your own, but they really should at least be Math methods IMO to take advantage of any native-level atomic operations that may be present.
33

You could create a class to represent this

public class Range
{
    private int low;
    private int high;

    public Range(int low, int high){
        this.low = low;
        this.high = high;
    }

    public boolean contains(int number){
        return (number >= low && number <= high);
    }
}

Sample usage:

Range range = new Range(0, 2147483647);

if (range.contains(foo)) {
    //do something
}

2 Comments

I now see you were trying to avoid the if. I thought you just didn't like the comparison syntax in the if... oh well.
With this implementation, you cannot represent an empty range (assuming low <= high). Either the interval must be half-open [low, high) or else use an extent instead of high.
24

I know this is quite an old question, but with Java 8's Streams you can get a range of ints like this:

// gives an IntStream of integers from 0 through Integer.MAX_VALUE
IntStream.rangeClosed(0, Integer.MAX_VALUE); 

Then you can do something like this:

if (IntStream.rangeClosed(0, Integer.MAX_VALUE).matchAny(n -> n == A)) {
    // do something
} else {
    // do something else 
}

2 Comments

This is a very slow solution. You are doing 2 billion comparisons rather than 2.
@Michael up to 2 billion comparisons, but only A in the best case. On the other hand, you need only one actual comparison, A >= 0 here.
14

You could use java.time.temporal.ValueRange which accepts long and would also work with int:

int a = 2147;

//Use java 8 java.time.temporal.ValueRange. The range defined
//is inclusive of both min and max 
ValueRange range = ValueRange.of(0, 2147483647);

if(range.isValidValue(a)) {
    System.out.println("in range");
}else {
    System.out.println("not in range");
}

1 Comment

I like this, unfortunately it's in the Time package but is usable in many other cases.
8

If you are checking against a lot of intervals, I suggest using an interval tree.

1 Comment

Is there a known Java library that implements an interval tree?
5

You will have an if-check no matter how efficient you try to optimize this not-so-intensive computation :) You can subtract the upper bound from the number and if it's positive you know you are out of range. You can perhaps perform some boolean bit-shift logic to figure it out and you can even use Fermat's theorem if you want (kidding :) But the point is "why" do you need to optimize this comparison? What's the purpose?

4 Comments

I'm with the OP - this kind of procedural check has always felt awkward. I prefer declarative contracts when available, even if the former eventually reduces to the latter.
I need to optimize because I find it much slower when let's say A is a really big number. And what if I choose to expand and do other checks within that if..else statement? >_<
You must be kidding -- slow? As in you have measured it to perform badly?
No... >_< I meant, I have OTHER checks within that if else statement, not just that... >_< That's why I'm thinking that if there was another way.
3

For a range of Comparable I use the following :

public class Range<T extends Comparable<T>> {

    /**
     * Include start, end in {@link Range}
     */
    public enum Inclusive {START,END,BOTH,NONE }

    /**
     * {@link Range} start and end values
     */
    private T start, end;
    private Inclusive inclusive;

    /**
     * Create a range with {@link Inclusive#START}
     * @param start
     *<br/> Not null safe
     * @param end
     *<br/> Not null safe
     */
    public Range(T start, T end) {  this(start, end, null); }

    /**
     * @param start
     *<br/> Not null safe
     * @param end
     *<br/> Not null safe
     *@param inclusive
     *<br/>If null {@link Inclusive#START} used
     */
    public Range(T start, T end, Inclusive inclusive) {

        if((start == null) || (end == null)) {
            throw new NullPointerException("Invalid null start / end value");
        }
        setInclusive(inclusive);

        if( isBigger(start, end) ) {
            this.start = end;   this.end   = start;
        }else {
            this.start = start;  this.end   = end;
        }
    }

    /**
     * Convenience method
     */
    public boolean isBigger(T t1, T t2) { return t1.compareTo(t2) > 0; }

    /**
     * Convenience method
     */
    public boolean isSmaller(T t1, T t2) { return t1.compareTo(t2) < 0; }

    /**
     * Check if this {@link Range} contains t
     *@param t
     *<br/>Not null safe
     *@return
     *false for any value of t, if this.start equals this.end
     */
    public boolean contains(T t) { return contains(t, inclusive); }

    /**
     * Check if this {@link Range} contains t
     *@param t
     *<br/>Not null safe
     *@param inclusive
     *<br/>If null {@link Range#inclusive} used
     *@return
     *false for any value of t, if this.start equals this.end
     */
    public boolean contains(T t, Inclusive inclusive) {

        if(t == null) {
            throw new NullPointerException("Invalid null value");
        }

        inclusive = (inclusive == null) ? this.inclusive : inclusive;

        switch (inclusive) {
            case NONE:
                return ( isBigger(t, start) && isSmaller(t, end) );
            case BOTH:
                return ( ! isBigger(start, t)  && ! isBigger(t, end) ) ;
            case START: default:
                return ( ! isBigger(start, t)  &&  isBigger(end, t) ) ;
            case END:
                return ( isBigger(t, start)  &&  ! isBigger(t, end) ) ;
        }
    }

    /**
     * Check if this {@link Range} contains other range
     * @return
     * false for any value of range, if this.start equals this.end
     */
    public boolean contains(Range<T> range) {
        return contains(range.start) && contains(range.end);
    }

    /**
     * Check if this {@link Range} intersects with other range
     * @return
     * false for any value of range, if this.start equals this.end
     */
    public boolean intersects(Range<T> range) {
        return contains(range.start) || contains(range.end);
    }

    /**
    * Get {@link #start}
    */
    public T getStart() { return start; }

    /**
    * Set {@link #start}
    * <br/>Not null safe
    * <br/>If start > end they are switched
    */
    public Range<T> setStart(T start) {

        if(start.compareTo(end)>0) {
            this.start = end;
            this.end  = start;
        }else {
            this.start = start;
        }
        return this;
    }

    /**
    * Get {@link #end}
    */
    public T getEnd() {  return end;  }

    /**
    * Set {@link #end}
    * <br/>Not null safe
    *  <br/>If start > end they are switched
    */
    public  Range<T> setEnd(T end) {

        if(start.compareTo(end)>0) {
            this.end  = start;
            this.start = end;
        }else {
            this.end = end;
        }
        return this;
    }

    /**
    * Get {@link #inclusive}
    */
    public Inclusive getInclusive() { return inclusive; }

    /**
    * Set {@link #inclusive}
    * @param inclusive
    *<br/>If null {@link Inclusive#START} used
    */
    public  Range<T> setInclusive(Inclusive inclusive) {

        this.inclusive = (inclusive == null) ? Inclusive.START : inclusive;
        return this;
    }
}

(This is a somewhat shorted version. The full code is available here )

Comments

0
import java.util.Arrays;

class Soft{
    public static void main(String[] args){
        int[] nums=range(9, 12);
        System.out.println(Arrays.toString(nums));
    }
    static int[] range(int low, int high){
        int[] a=new int[high-low];
        for(int i=0,j=low;i<high-low;i++,j++){
            a[i]=j;
        }
        return a;

    }
}

My code is similar to Python`s range :)

Comments

0

If you use Spring, you can rely on org.springframework.data.domain that is quite complete including bound and unbound ranges.

Comments

0

Of course, there is nothing wrong with

 return (i >= lo && i <= hi);

And, as @Holger pointed out in a comment,

 return (foo >= 0);

is sufficient, if hi=Integer.MAX_VALUE;

It might look more efficient, to do bitshifting, but I doubt it:

 return (i >> 31) == 0;

@ScArcher suggested a general class, which is fine for 2011, but could nowadays been implemented with a record, saving us 5 lines of code while giving us a nice toString and hashCode and equals-method for free. And while we're already probably overengineering this thing, let's use a Lambda Predicate (which cost us the addiditional line for the import:

import java.util.function.*; 

public record InRange (int lo, int hi) implements IntPredicate {
    public boolean test (int i) {
        return i >= lo && i <= hi;
    }
}

// usage: 
InRange posInt = new InRange (0, Integer.MAX_VALUE);
posInt.test (42); // true
posInt.test (-42); // false
List.of (Integer.MIN_VALUE, -2, -1, 0, 1, 2, Integer.MAX_VALUE).stream ().filter (posInt::test).toList();
$71 ==> [0, 1, 2, 2147483647]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.