【问题标题】:How can I represent a range in Java?如何在 Java 中表示范围?
【发布时间】:2011-10-11 04:19:25
【问题描述】:

假设一个整数应该在以下范围内:[0...2147483647]

我想检查一个整数变量是否在这个范围内。我知道它可以通过一个简单的 if-else 语句来完成,但是有没有更有效的方法来检查它是否在范围内?

我不想这样做:

if (foo >= 0 && foo <= 2147483647) 
{
    // do something
}

【问题讨论】:

  • 如果您要求运行时效率(性能),恐怕您的if 语句会胜出。不过,我同意这种观察不应该推动你的决定。对于 99 % 的应用,维护效率更为重要。
  • @OleV.V.我什至将它简化为if(foo &gt;= 0) …,因为根本不需要检查int 是否是&lt;=Integer.MAX_VALUE。但我怀疑提问者是否会读到这些 cmets,因为在缺席五年后,再回来的可能性不大。
  • @OleV.V.,必须注意调用的频率和方式。流水线在条件条件下被 100% 压缩。如果您可以通过其他一些“固态”数学魔法来强制限制范围,那么您会过得更好。

标签: java


【解决方案1】:

Apache Commons Lang 有一个 Range class 用于执行任意范围。

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

Guava Range 有类似的 API。

如果您只是想检查一个数字是否适合 long 值或 int 值,您可以尝试通过BigDecimal 使用它。 longValueExactintValueExact 有一些方法,如果值对于这些精度而言太大,则会引发异常。

【讨论】:

  • 是的,但是您认为.contains(...) 是如何实现的? ;)(肯定有 if/else :)
  • 你知道它是否能够处理外边缘。 (int 示例)类似 = 12 的东西?
  • 回答我自己的问题。 “是”就是答案。请参阅:Range.atMost 和 Range.atLeast。 guava.dev/releases/snapshot/api/docs/com/google/common/collect/…
  • 这个Range 的问题是它似乎缺少类似于addWrap()subtractWrap() 的概念,当您需要向下减/减时,这两个概念都非常有用低于最小值的值包裹到最大值,类似的用于 inc/添加一个超过最大值时包裹的值。如果需要,您甚至可以使用 % 并允许使用浮点数。这些很容易自己实现,但它们至少应该是 Math 方法 IMO 以利用可能存在的任何本机级别的原子操作。
【解决方案2】:

你可以创建一个类来表示这个

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);
    }
}

示例用法:

Range range = new Range(0, 2147483647);

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

【讨论】:

  • 我现在看到你试图避免 if。我以为你只是不喜欢 if... 中的比较语法。哦,好吧。
  • 使用这个实现,你不能代表一个空范围(假设low &lt;= high)。间隔必须是半开的[low, high),或者使用extent 而不是high
【解决方案3】:

我知道这是一个很老的问题,但是使用 Java 8 的 Streams,您可以获得ints 的范围,如下所示:

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

然后你可以这样做:

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

【讨论】:

  • 这是一个非常缓慢的解决方案。您正在进行 20 亿次比较,而不是 2 次。
  • @Michael 最多 20亿次比较,但最好的情况下只有A。另一方面,您只需要 一个 实际比较,A &gt;= 0 这里。
【解决方案4】:

您可以使用接受longjava.time.temporal.ValueRange,也可以使用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");
}

【讨论】:

  • 我喜欢这个,不幸的是它在 Time 包中,但在许多其他情况下也可以使用。
【解决方案5】:

如果您要检查很多间隔,我建议使用interval tree

【讨论】:

  • 是否有已知的 Java 库可以实现区间树?
【解决方案6】:

无论您尝试优化这种不那么密集的计算有多有效,您都将进行 if-check :) 您可以从数字中减去上限,如果它是正数,您就知道您超出了范围。您也许可以执行一些布尔移位逻辑来解决这个问题,如果您愿意,您甚至可以使用费马定理(开玩笑 :) 但关键是“为什么”需要优化这种比较?目的是什么?

【讨论】:

  • 我和 OP 在一起——这种程序检查总是让人感到尴尬。我更喜欢声明式合同,即使前者最终会减少为后者。
  • 我需要优化,因为当假设 A 是一个非常大的数字时,我发现它要慢得多。如果我选择在 if..else 语句中扩展并进行其他检查怎么办? >_
  • 你一定是在开玩笑——慢?就像您测量它表现不佳一样?
  • 不... >__
【解决方案7】:

对于Comparable 的范围,我使用以下内容:

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;
    }
}

(这是一个略短的版本。完整代码可在here 获得)

【讨论】:

    【解决方案8】:
    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;
    
        }
    }
    

    我的代码类似于 Python 的范围 :)

    【讨论】:

      【解决方案9】:

      如果您使用 Spring,则可以依赖 org.springframework.data.domain,它非常完整,包括 bound 和 unbound 范围。

      【讨论】:

        猜你喜欢
        • 2016-05-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-11
        • 1970-01-01
        • 2023-03-09
        • 1970-01-01
        相关资源
        最近更新 更多