2023-08-17
原文作者:LifeIsForSharing 原文地址:https://solang.blog.csdn.net/article/details/105101316

Ranges:Guava强大的API,用于处理连续和离散的可Comparable比较类型的区间。

1.示例

    List<Double> scores;
    Iterable<Double> belowMedianScores = Iterables.filter(scores, Range.lessThan(median));
    ...
    Range<Integer> validGrades = Range.closed(1, 12);
    for(int grade : ContiguousSet.create(validGrades, DiscreteDomain.integers())) {
      ...
    }

2.介绍

区间(有时称为间隔)是特定域的凸(非正式地称为"连续的"或"不间断的")部分。从形式上说,凸度表示对于任何a <= b <= crange.contains(a) && range.contains(c)意味着range.contains(b)

区间可以“扩展到无穷大”——例如,区间x > 3包含任意大的值——或可以受到有限约束,例如2 <= x < 5。我们将使用更精简的表示法,这是具有数学背景的程序员所熟悉的:

  • (a..b) = {x | a < x < b}
  • [a..b] = {x | a <= x <= b}
  • [a..b) = {x | a <= x < b}
  • (a..b] = {x | a < x <= b}
  • (a..+∞) = {x | x > a}
  • [a..+∞) = {x | x >= a}
  • (-∞..b) = {x | x < b}
  • (-∞..b] = {x | x <= b}
  • (-∞..+∞) = all values

上面使用的值a和b称为端点。为了提高一致性,Guava的Range概念要求上端点不能小于下端点。仅当至少一个边界闭合时,上下端点才可能相等:

  • [a..a]: 单重区间
  • [a..a); (a..a]: 空,但有效
  • (a..a): 无效

Guava中区间的类型为Range<C>。所有区间都是不可变的

3.构建区间

可以从Range上的静态方法获得区间:

区间类型 方法
(a..b) open(C,C)
[a..b] closed(C,C)
[a..b) closedOpen(C,C)
(a..b] openClosed(C,C)
(a..+∞) greaterThan(C)
[a..+∞) atLeast(C)
(-∞..b) lessThan(C)
(-∞..b] atMost(C)
(-∞..+∞) all()
    Range.closed("left", "right"); // all strings lexographically between "left" and "right" inclusive
    Range.lessThan(4.0); // double values strictly less than 4

此外,可以通过显式传递边界类型来构造Range实例:

区间类型 方法
两端有界 range(C,BoundType,C,BoundType)
上限无界((a..+∞)或[a..+∞)) downTo(C,BoundType)
下限无界((-∞..b)或(-∞..b]) upTo(C,BoundType)

在这里,BoundType是一个包含值CLOSEDOPEN的枚举。

    Range.downTo(4, boundType); // allows you to decide whether or not you want to include 4
    Range.range(1, CLOSED, 4, OPEN); // another way of writing Range.closedOpen(1, 4)

4.操作

Range的基本操作是其contains(C)方法,其行为与你预期的完全一样。此外,Range可以用作Predicate谓语,并用于函数式语法。任何Range也支持containsAll(Iterable<? extends C>)

    Range.closed(1, 3).contains(2); // returns true
    Range.closed(1, 3).contains(4); // returns false
    Range.lessThan(5).contains(5); // returns false
    Range.closed(1, 4).containsAll(Ints.asList(1, 2, 3)); // returns true

4.1查询操作

为了查看区间的端点,Range公开了以下方法:

    Range.closedOpen(4, 4).isEmpty(); // returns true
    Range.openClosed(4, 4).isEmpty(); // returns true
    Range.closed(4, 4).isEmpty(); // returns false
    Range.open(4, 4).isEmpty(); // Range.open throws IllegalArgumentException
    
    Range.closed(3, 10).lowerEndpoint(); // returns 3
    Range.open(3, 10).lowerEndpoint(); // returns 3
    Range.closed(3, 10).lowerBoundType(); // returns CLOSED
    Range.open(3, 10).upperBoundType(); // returns OPEN

4.2运算操作

4.2.1encloses

区间上最基本的关系是encloses(Range),如果内部区间的边界没有超出外部区间的边界之外,则为true。这完全取决于端点之间的比较!

  • [3..6]包围[4..5]
  • (3..6)包围(3..6)
  • [3..6]包围[4..4)(即使后者为空)
  • (3..6]不包围[3..6]
  • [4..5]不包围(3..6) 尽管它包含后一个区间包含的每个值 ,但使用离散域可以解决此问题(参见下文)
  • [3..6]不包围(1..1] 尽管它包含后一个区间包含的每个值

encloses部分排序

鉴于此,Range提供以下操作:

4.2.2isConnected

Range.isConnected(Range),测试这些区间是否连接。具体来说,isConnected测试是否有某个区间包围这两个区间,但这等同于数学定义,即区间的并集必须形成一个连接集(空区间的特殊情况除外)。

isConnected自反的对称的 关系

    Range.closed(3, 5).isConnected(Range.open(5, 10)); // returns true
    Range.closed(0, 9).isConnected(Range.closed(3, 4)); // returns true
    Range.closed(0, 5).isConnected(Range.closed(3, 9)); // returns true
    Range.open(3, 5).isConnected(Range.open(5, 10)); // returns false
    Range.closed(1, 5).isConnected(Range.closed(6, 10)); // returns false

4.2.3intersection

Range.intersection(Range)返回此区间和其他区间所包围的最大区间(如果这些区间是连接的,则存在),或者如果不存在这样的区间,则抛出IllegalArgumentException

交集是可交换的关联的[运算] [二进制运算]。

    Range.closed(3, 5).intersection(Range.open(5, 10)); // returns (5, 5]
    Range.closed(0, 9).intersection(Range.closed(3, 4)); // returns [3, 4]
    Range.closed(0, 5).intersection(Range.closed(3, 9)); // returns [3, 5]
    Range.open(3, 5).intersection(Range.open(5, 10)); // throws IAE
    Range.closed(1, 5).intersection(Range.closed(6, 10)); // throws IAE

4.2.4span

Range.span(Range)返回此区间和其他区间所包围的最小区间。如果两个区间都连接,则这是它们的并集。

span可交换的关联的闭合的[运算] [二进制运算]。

    Range.closed(3, 5).span(Range.open(5, 10)); // returns [3, 10)
    Range.closed(0, 9).span(Range.closed(3, 4)); // returns [0, 9]
    Range.closed(0, 5).span(Range.closed(3, 9)); // returns [0, 9]
    Range.open(3, 5).span(Range.open(5, 10)); // returns (3, 10)
    Range.closed(1, 5).span(Range.closed(6, 10)); // returns [1, 10]

5.离散域

有些类型(但不是全部可比较类型)是离散的,这意味着可以枚举两边有界的区间。

在Guava中,DiscreteDomain<C>C类型实现了离散操作。离散域始终代表其类型的整个值集;它不能表示部分域,例如“整型素数”,“长度为5的字符串”或“午夜的时间戳”。

DiscreteDomain类提供DiscreteDomain实例:

类型 离散域
Integer integers()
Long longs()

一旦有DiscreteDomain后,可以使用以下Range操作:

  • ContiguousSet.create(range, domain):以ImmutableSortedSet的形式查看Range<C>,并加入一些额外的操作。(除非类型本身是有界的,否则不适用于无界区间)
  • canonical(domain):以“规范形式”放置区间。如果ContiguousSet.create(a, domain).equals(ContiguousSet.create(b, domain))!a.isEmpty(),则a.canonical(domain).equals(b.canonical(domain))。(但是,这并意味着a.equals(b)
    ImmutableSortedSet<Integer> set = ContiguousSet.create(Range.open(1, 5), DiscreteDomain.integers());
    // set contains [2, 3, 4]
    
    ContiguousSet.create(Range.greaterThan(0), DiscreteDomain.integers());
    // set contains [1, 2, ..., Integer.MAX_VALUE]

注意,ContiguousSet.create实际上并不构造整个区间,而是将区间视图作为集合返回。

5.1你自己的离散域

你可以创建自己的DiscreteDomain对象,但是必须记住DiscreteDomain契约的几个重要方面。

  • 离散域始终代表其类型的整个值集;它不能表示部分域,例如“整型素数”或“长度为5的字符串”。例如,你不能构造一个DiscreteDomain来查看一个区间内的日期集,其JODA DateTime包含的时间不超过秒:因为它不包含该类型的所有元素。
  • DiscreteDomain可以是无限的——例如,BigInteger DiscreteDomain。在这种情况下,应使用minValue()maxValue()的默认实现,它们会抛出NoSuchElementException。但是,这禁止你在无限区间内使用ContiguousSet.create方法!

6.如果需要Comparator怎么办?

我们希望在Range的能力与API复杂度之间取得非常明确的平衡,其中一部分涉及不提供基于Comparator的接口:我们不必担心基于不同比较器的区间如何相互作用; API签名都显著地简化;事情变的更好。

另一方面,如果你想使用任意Comparator,则可以执行以下操作之一:

  • 使用通用Predicate而不是Range。(由于Range实现了Predicate接口,因此可以使用Predicates.compose(range, function)来获取Predicate。)
  • 在对象周围使用包装器类来定义所需的顺序。

7.示例代码

    import com.google.common.base.Predicate;
    import com.google.common.collect.*;
    import com.google.common.testing.EqualsTester;
    import junit.framework.TestCase;
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.List;
    import java.util.NoSuchElementException;
    
    import static com.google.common.collect.BoundType.CLOSED;
    import static com.google.common.collect.BoundType.OPEN;
    import static com.google.common.collect.DiscreteDomain.integers;
    import static com.google.common.testing.SerializableTester.reserializeAndAssert;
    import static java.util.Arrays.asList;
    
    public class RangeTest extends TestCase {
    
        public void testOpen() {
            Range<Integer> range = Range.open(4, 8);
            checkContains(range);
            assertTrue(range.hasLowerBound());
            assertEquals(4, (int) range.lowerEndpoint());
            assertEquals(OPEN, range.lowerBoundType());
            assertTrue(range.hasUpperBound());
            assertEquals(8, (int) range.upperEndpoint());
            assertEquals(OPEN, range.upperBoundType());
            assertFalse(range.isEmpty());
            assertEquals("(4..8)", range.toString());
            reserializeAndAssert(range);
        }
    
        public void testOpen_invalid() {
            try {
                Range.open(4, 3);
                fail();
            } catch (IllegalArgumentException expected) {
            }
            try {
                Range.open(3, 3);
                fail();
            } catch (IllegalArgumentException expected) {
            }
        }
    
        public void testClosed() {
            Range<Integer> range = Range.closed(5, 7);
            checkContains(range);
            assertTrue(range.hasLowerBound());
            assertEquals(5, (int) range.lowerEndpoint());
            assertEquals(CLOSED, range.lowerBoundType());
            assertTrue(range.hasUpperBound());
            assertEquals(7, (int) range.upperEndpoint());
            assertEquals(CLOSED, range.upperBoundType());
            assertFalse(range.isEmpty());
            assertEquals("[5..7]", range.toString());
            reserializeAndAssert(range);
        }
    
        public void testClosed_invalid() {
            try {
                Range.closed(4, 3);
                fail();
            } catch (IllegalArgumentException expected) {
            }
        }
    
        public void testOpenClosed() {
            Range<Integer> range = Range.openClosed(4, 7);
            checkContains(range);
            assertTrue(range.hasLowerBound());
            assertEquals(4, (int) range.lowerEndpoint());
            assertEquals(OPEN, range.lowerBoundType());
            assertTrue(range.hasUpperBound());
            assertEquals(7, (int) range.upperEndpoint());
            assertEquals(CLOSED, range.upperBoundType());
            assertFalse(range.isEmpty());
            assertEquals("(4..7]", range.toString());
            reserializeAndAssert(range);
        }
    
        public void testClosedOpen() {
            Range<Integer> range = Range.closedOpen(5, 8);
            checkContains(range);
            assertTrue(range.hasLowerBound());
            assertEquals(5, (int) range.lowerEndpoint());
            assertEquals(CLOSED, range.lowerBoundType());
            assertTrue(range.hasUpperBound());
            assertEquals(8, (int) range.upperEndpoint());
            assertEquals(OPEN, range.upperBoundType());
            assertFalse(range.isEmpty());
            assertEquals("[5..8)", range.toString());
            reserializeAndAssert(range);
        }
    
        public void testIsConnected() {
            assertTrue(Range.closed(3, 5).isConnected(Range.open(5, 6)));
            assertTrue(Range.closed(3, 5).isConnected(Range.openClosed(5, 5)));
            assertTrue(Range.open(3, 5).isConnected(Range.closed(5, 6)));
            assertTrue(Range.closed(3, 7).isConnected(Range.open(6, 8)));
            assertTrue(Range.open(3, 7).isConnected(Range.closed(5, 6)));
            assertFalse(Range.closed(3, 5).isConnected(Range.closed(7, 8)));
            assertFalse(Range.closed(3, 5).isConnected(Range.closedOpen(7, 7)));
        }
    
        private static void checkContains(Range<Integer> range) {
            assertFalse(range.contains(4));
            assertTrue(range.contains(5));
            assertTrue(range.contains(7));
            assertFalse(range.contains(8));
        }
    
        public void testSingleton() {
            Range<Integer> range = Range.closed(4, 4);
            assertFalse(range.contains(3));
            assertTrue(range.contains(4));
            assertFalse(range.contains(5));
            assertTrue(range.hasLowerBound());
            assertEquals(4, (int) range.lowerEndpoint());
            assertEquals(CLOSED, range.lowerBoundType());
            assertTrue(range.hasUpperBound());
            assertEquals(4, (int) range.upperEndpoint());
            assertEquals(CLOSED, range.upperBoundType());
            assertFalse(range.isEmpty());
            assertEquals("[4..4]", range.toString());
            reserializeAndAssert(range);
        }
    
        public void testEmpty1() {
            Range<Integer> range = Range.closedOpen(4, 4);
            assertFalse(range.contains(3));
            assertFalse(range.contains(4));
            assertFalse(range.contains(5));
            assertTrue(range.hasLowerBound());
            assertEquals(4, (int) range.lowerEndpoint());
            assertEquals(CLOSED, range.lowerBoundType());
            assertTrue(range.hasUpperBound());
            assertEquals(4, (int) range.upperEndpoint());
            assertEquals(OPEN, range.upperBoundType());
            assertTrue(range.isEmpty());
            assertEquals("[4..4)", range.toString());
            reserializeAndAssert(range);
        }
    
        public void testEmpty2() {
            Range<Integer> range = Range.openClosed(4, 4);
            assertFalse(range.contains(3));
            assertFalse(range.contains(4));
            assertFalse(range.contains(5));
            assertTrue(range.hasLowerBound());
            assertEquals(4, (int) range.lowerEndpoint());
            assertEquals(OPEN, range.lowerBoundType());
            assertTrue(range.hasUpperBound());
            assertEquals(4, (int) range.upperEndpoint());
            assertEquals(CLOSED, range.upperBoundType());
            assertTrue(range.isEmpty());
            assertEquals("(4..4]", range.toString());
            reserializeAndAssert(range);
        }
    
        public void testLessThan() {
            Range<Integer> range = Range.lessThan(5);
            assertTrue(range.contains(Integer.MIN_VALUE));
            assertTrue(range.contains(4));
            assertFalse(range.contains(5));
            assertUnboundedBelow(range);
            assertTrue(range.hasUpperBound());
            assertEquals(5, (int) range.upperEndpoint());
            assertEquals(OPEN, range.upperBoundType());
            assertFalse(range.isEmpty());
            assertEquals("(-\u221e..5)", range.toString());
            reserializeAndAssert(range);
        }
    
        public void testGreaterThan() {
            Range<Integer> range = Range.greaterThan(5);
            assertFalse(range.contains(5));
            assertTrue(range.contains(6));
            assertTrue(range.contains(Integer.MAX_VALUE));
            assertTrue(range.hasLowerBound());
            assertEquals(5, (int) range.lowerEndpoint());
            assertEquals(OPEN, range.lowerBoundType());
            assertUnboundedAbove(range);
            assertFalse(range.isEmpty());
            assertEquals("(5..+\u221e)", range.toString());
            reserializeAndAssert(range);
        }
    
        public void testAtLeast() {
            Range<Integer> range = Range.atLeast(6);
            assertFalse(range.contains(5));
            assertTrue(range.contains(6));
            assertTrue(range.contains(Integer.MAX_VALUE));
            assertTrue(range.hasLowerBound());
            assertEquals(6, (int) range.lowerEndpoint());
            assertEquals(CLOSED, range.lowerBoundType());
            assertUnboundedAbove(range);
            assertFalse(range.isEmpty());
            assertEquals("[6..+\u221e)", range.toString());
            reserializeAndAssert(range);
        }
    
        public void testAtMost() {
            Range<Integer> range = Range.atMost(4);
            assertTrue(range.contains(Integer.MIN_VALUE));
            assertTrue(range.contains(4));
            assertFalse(range.contains(5));
            assertUnboundedBelow(range);
            assertTrue(range.hasUpperBound());
            assertEquals(4, (int) range.upperEndpoint());
            assertEquals(CLOSED, range.upperBoundType());
            assertFalse(range.isEmpty());
            assertEquals("(-\u221e..4]", range.toString());
            reserializeAndAssert(range);
        }
    
        public void testAll() {
            Range<Integer> range = Range.all();
            assertTrue(range.contains(Integer.MIN_VALUE));
            assertTrue(range.contains(Integer.MAX_VALUE));
            assertUnboundedBelow(range);
            assertUnboundedAbove(range);
            assertFalse(range.isEmpty());
            assertEquals("(-\u221e..+\u221e)", range.toString());
            assertSame(range, reserializeAndAssert(range));
            assertSame(range, Range.all());
        }
    
        private static void assertUnboundedBelow(Range<Integer> range) {
            assertFalse(range.hasLowerBound());
            try {
                range.lowerEndpoint();
                fail();
            } catch (IllegalStateException expected) {
            }
            try {
                range.lowerBoundType();
                fail();
            } catch (IllegalStateException expected) {
            }
        }
    
        private static void assertUnboundedAbove(Range<Integer> range) {
            assertFalse(range.hasUpperBound());
            try {
                range.upperEndpoint();
                fail();
            } catch (IllegalStateException expected) {
            }
            try {
                range.upperBoundType();
                fail();
            } catch (IllegalStateException expected) {
            }
        }
    
        public void testContainsAll() {
            Range<Integer> range = Range.closed(3, 5);
            assertTrue(range.containsAll(asList(3, 3, 4, 5)));
            assertFalse(range.containsAll(asList(3, 3, 4, 5, 6)));
    
            // We happen to know that natural-order sorted sets use a different code
            // path, so we test that separately
            assertTrue(range.containsAll(ImmutableSortedSet.of(3, 3, 4, 5)));
            assertTrue(range.containsAll(ImmutableSortedSet.of(3)));
            assertTrue(range.containsAll(ImmutableSortedSet.<Integer>of()));
            assertFalse(range.containsAll(ImmutableSortedSet.of(3, 3, 4, 5, 6)));
    
            assertTrue(Range.openClosed(3, 3).containsAll(Collections.<Integer>emptySet()));
        }
    
        public void testEncloses_open() {
            Range<Integer> range = Range.open(2, 5);
            assertTrue(range.encloses(range));
            assertTrue(range.encloses(Range.open(2, 4)));
            assertTrue(range.encloses(Range.open(3, 5)));
            assertTrue(range.encloses(Range.closed(3, 4)));
    
            assertFalse(range.encloses(Range.openClosed(2, 5)));
            assertFalse(range.encloses(Range.closedOpen(2, 5)));
            assertFalse(range.encloses(Range.closed(1, 4)));
            assertFalse(range.encloses(Range.closed(3, 6)));
            assertFalse(range.encloses(Range.greaterThan(3)));
            assertFalse(range.encloses(Range.lessThan(3)));
            assertFalse(range.encloses(Range.atLeast(3)));
            assertFalse(range.encloses(Range.atMost(3)));
            assertFalse(range.encloses(Range.<Integer>all()));
        }
    
        public void testEncloses_closed() {
            Range<Integer> range = Range.closed(2, 5);
            assertTrue(range.encloses(range));
            assertTrue(range.encloses(Range.open(2, 5)));
            assertTrue(range.encloses(Range.openClosed(2, 5)));
            assertTrue(range.encloses(Range.closedOpen(2, 5)));
            assertTrue(range.encloses(Range.closed(3, 5)));
            assertTrue(range.encloses(Range.closed(2, 4)));
    
            assertFalse(range.encloses(Range.open(1, 6)));
            assertFalse(range.encloses(Range.greaterThan(3)));
            assertFalse(range.encloses(Range.lessThan(3)));
            assertFalse(range.encloses(Range.atLeast(3)));
            assertFalse(range.encloses(Range.atMost(3)));
            assertFalse(range.encloses(Range.<Integer>all()));
        }
    
        public void testIntersection_empty() {
            Range<Integer> range = Range.closedOpen(3, 3);
            assertEquals(range, range.intersection(range));
    
            try {
                range.intersection(Range.open(3, 5));
                fail();
            } catch (IllegalArgumentException expected) {
            }
            try {
                range.intersection(Range.closed(0, 2));
                fail();
            } catch (IllegalArgumentException expected) {
            }
        }
    
        public void testIntersection_deFactoEmpty() {
            Range<Integer> range = Range.open(3, 4);
            assertEquals(range, range.intersection(range));
    
            assertEquals(Range.openClosed(3, 3), range.intersection(Range.atMost(3)));
            assertEquals(Range.closedOpen(4, 4), range.intersection(Range.atLeast(4)));
    
            try {
                range.intersection(Range.lessThan(3));
                fail();
            } catch (IllegalArgumentException expected) {
            }
            try {
                range.intersection(Range.greaterThan(4));
                fail();
            } catch (IllegalArgumentException expected) {
            }
    
            range = Range.closed(3, 4);
            assertEquals(Range.openClosed(4, 4), range.intersection(Range.greaterThan(4)));
        }
    
        public void testIntersection_singleton() {
            Range<Integer> range = Range.closed(3, 3);
            assertEquals(range, range.intersection(range));
    
            assertEquals(range, range.intersection(Range.atMost(4)));
            assertEquals(range, range.intersection(Range.atMost(3)));
            assertEquals(range, range.intersection(Range.atLeast(3)));
            assertEquals(range, range.intersection(Range.atLeast(2)));
    
            assertEquals(Range.closedOpen(3, 3), range.intersection(Range.lessThan(3)));
            assertEquals(Range.openClosed(3, 3), range.intersection(Range.greaterThan(3)));
    
            try {
                range.intersection(Range.atLeast(4));
                fail();
            } catch (IllegalArgumentException expected) {
            }
            try {
                range.intersection(Range.atMost(2));
                fail();
            } catch (IllegalArgumentException expected) {
            }
        }
    
        public void testIntersection_general() {
            Range<Integer> range = Range.closed(4, 8);
    
            // separate below
            try {
                range.intersection(Range.closed(0, 2));
                fail();
            } catch (IllegalArgumentException expected) {
            }
    
            // adjacent below
            assertEquals(Range.closedOpen(4, 4), range.intersection(Range.closedOpen(2, 4)));
    
            // overlap below
            assertEquals(Range.closed(4, 6), range.intersection(Range.closed(2, 6)));
    
            // enclosed with same start
            assertEquals(Range.closed(4, 6), range.intersection(Range.closed(4, 6)));
    
            // enclosed, interior
            assertEquals(Range.closed(5, 7), range.intersection(Range.closed(5, 7)));
    
            // enclosed with same end
            assertEquals(Range.closed(6, 8), range.intersection(Range.closed(6, 8)));
    
            // equal
            assertEquals(range, range.intersection(range));
    
            // enclosing with same start
            assertEquals(range, range.intersection(Range.closed(4, 10)));
    
            // enclosing with same end
            assertEquals(range, range.intersection(Range.closed(2, 8)));
    
            // enclosing, exterior
            assertEquals(range, range.intersection(Range.closed(2, 10)));
    
            // overlap above
            assertEquals(Range.closed(6, 8), range.intersection(Range.closed(6, 10)));
    
            // adjacent above
            assertEquals(Range.openClosed(8, 8), range.intersection(Range.openClosed(8, 10)));
    
            // separate above
            try {
                range.intersection(Range.closed(10, 12));
                fail();
            } catch (IllegalArgumentException expected) {
            }
        }
    
        public void testGap_overlapping() {
            Range<Integer> range = Range.closedOpen(3, 5);
    
            try {
                range.gap(Range.closed(4, 6));
                fail();
            } catch (IllegalArgumentException expected) {
            }
            try {
                range.gap(Range.closed(2, 4));
                fail();
            } catch (IllegalArgumentException expected) {
            }
            try {
                range.gap(Range.closed(2, 3));
                fail();
            } catch (IllegalArgumentException expected) {
            }
        }
    
        public void testGap_connectedAdjacentYieldsEmpty() {
            Range<Integer> range = Range.open(3, 4);
    
            assertEquals(Range.closedOpen(4, 4), range.gap(Range.atLeast(4)));
            assertEquals(Range.openClosed(3, 3), range.gap(Range.atMost(3)));
        }
    
        public void testGap_general() {
            Range<Integer> openRange = Range.open(4, 8);
            Range<Integer> closedRange = Range.closed(4, 8);
    
            // first range open end, second range open start
            assertEquals(Range.closed(2, 4), Range.lessThan(2).gap(openRange));
            assertEquals(Range.closed(2, 4), openRange.gap(Range.lessThan(2)));
    
            // first range closed end, second range open start
            assertEquals(Range.openClosed(2, 4), Range.atMost(2).gap(openRange));
            assertEquals(Range.openClosed(2, 4), openRange.gap(Range.atMost(2)));
    
            // first range open end, second range closed start
            assertEquals(Range.closedOpen(2, 4), Range.lessThan(2).gap(closedRange));
            assertEquals(Range.closedOpen(2, 4), closedRange.gap(Range.lessThan(2)));
    
            // first range closed end, second range closed start
            assertEquals(Range.open(2, 4), Range.atMost(2).gap(closedRange));
            assertEquals(Range.open(2, 4), closedRange.gap(Range.atMost(2)));
        }
    
        public void testSpan_general() {
            Range<Integer> range = Range.closed(4, 8);
    
            // separate below
            assertEquals(Range.closed(0, 8), range.span(Range.closed(0, 2)));
            assertEquals(Range.atMost(8), range.span(Range.atMost(2)));
    
            // adjacent below
            assertEquals(Range.closed(2, 8), range.span(Range.closedOpen(2, 4)));
            assertEquals(Range.atMost(8), range.span(Range.lessThan(4)));
    
            // overlap below
            assertEquals(Range.closed(2, 8), range.span(Range.closed(2, 6)));
            assertEquals(Range.atMost(8), range.span(Range.atMost(6)));
    
            // enclosed with same start
            assertEquals(range, range.span(Range.closed(4, 6)));
    
            // enclosed, interior
            assertEquals(range, range.span(Range.closed(5, 7)));
    
            // enclosed with same end
            assertEquals(range, range.span(Range.closed(6, 8)));
    
            // equal
            assertEquals(range, range.span(range));
    
            // enclosing with same start
            assertEquals(Range.closed(4, 10), range.span(Range.closed(4, 10)));
            assertEquals(Range.atLeast(4), range.span(Range.atLeast(4)));
    
            // enclosing with same end
            assertEquals(Range.closed(2, 8), range.span(Range.closed(2, 8)));
            assertEquals(Range.atMost(8), range.span(Range.atMost(8)));
    
            // enclosing, exterior
            assertEquals(Range.closed(2, 10), range.span(Range.closed(2, 10)));
            assertEquals(Range.<Integer>all(), range.span(Range.<Integer>all()));
    
            // overlap above
            assertEquals(Range.closed(4, 10), range.span(Range.closed(6, 10)));
            assertEquals(Range.atLeast(4), range.span(Range.atLeast(6)));
    
            // adjacent above
            assertEquals(Range.closed(4, 10), range.span(Range.openClosed(8, 10)));
            assertEquals(Range.atLeast(4), range.span(Range.greaterThan(8)));
    
            // separate above
            assertEquals(Range.closed(4, 12), range.span(Range.closed(10, 12)));
            assertEquals(Range.atLeast(4), range.span(Range.atLeast(10)));
        }
    
        public void testApply() {
            Predicate<Integer> predicate = Range.closed(2, 3);
            assertFalse(predicate.apply(1));
            assertTrue(predicate.apply(2));
            assertTrue(predicate.apply(3));
            assertFalse(predicate.apply(4));
        }
    
        public void testEquals() {
            new EqualsTester()
                    .addEqualityGroup(Range.open(1, 5), Range.range(1, OPEN, 5, OPEN))
                    .addEqualityGroup(Range.greaterThan(2), Range.greaterThan(2))
                    .addEqualityGroup(Range.all(), Range.all())
                    .addEqualityGroup("Phil")
                    .testEquals();
        }
    
        static final DiscreteDomain<Integer> UNBOUNDED_DOMAIN =
                new DiscreteDomain<Integer>() {
                    @Override
                    public Integer next(Integer value) {
                        return integers().next(value);
                    }
    
                    @Override
                    public Integer previous(Integer value) {
                        return integers().previous(value);
                    }
    
                    @Override
                    public long distance(Integer start, Integer end) {
                        return integers().distance(start, end);
                    }
                };
    
        public void testCanonical() {
            assertEquals(Range.closedOpen(1, 5), Range.closed(1, 4).canonical(integers()));
            assertEquals(Range.closedOpen(1, 5), Range.open(0, 5).canonical(integers()));
            assertEquals(Range.closedOpen(1, 5), Range.closedOpen(1, 5).canonical(integers()));
            assertEquals(Range.closedOpen(1, 5), Range.openClosed(0, 4).canonical(integers()));
    
            assertEquals(
                    Range.closedOpen(Integer.MIN_VALUE, 0),
                    Range.closedOpen(Integer.MIN_VALUE, 0).canonical(integers()));
    
            assertEquals(Range.closedOpen(Integer.MIN_VALUE, 0), Range.lessThan(0).canonical(integers()));
            assertEquals(Range.closedOpen(Integer.MIN_VALUE, 1), Range.atMost(0).canonical(integers()));
            assertEquals(Range.atLeast(0), Range.atLeast(0).canonical(integers()));
            assertEquals(Range.atLeast(1), Range.greaterThan(0).canonical(integers()));
    
            assertEquals(Range.atLeast(Integer.MIN_VALUE), Range.<Integer>all().canonical(integers()));
        }
    
        public void testCanonical_unboundedDomain() {
            assertEquals(Range.lessThan(0), Range.lessThan(0).canonical(UNBOUNDED_DOMAIN));
            assertEquals(Range.lessThan(1), Range.atMost(0).canonical(UNBOUNDED_DOMAIN));
            assertEquals(Range.atLeast(0), Range.atLeast(0).canonical(UNBOUNDED_DOMAIN));
            assertEquals(Range.atLeast(1), Range.greaterThan(0).canonical(UNBOUNDED_DOMAIN));
    
            assertEquals(Range.all(), Range.<Integer>all().canonical(UNBOUNDED_DOMAIN));
        }
    
        public void testEncloseAll() {
            assertEquals(Range.closed(0, 0), Range.encloseAll(Arrays.asList(0)));
            assertEquals(Range.closed(-3, 5), Range.encloseAll(Arrays.asList(5, -3)));
            assertEquals(Range.closed(-3, 5), Range.encloseAll(Arrays.asList(1, 2, 2, 2, 5, -3, 0, -1)));
        }
    
        public void testEncloseAll_empty() {
            try {
                Range.encloseAll(ImmutableSet.<Integer>of());
                fail();
            } catch (NoSuchElementException expected) {
            }
        }
    
        public void testEncloseAll_nullValue() {
            List<Integer> nullFirst = Lists.newArrayList(null, 0);
            try {
                Range.encloseAll(nullFirst);
                fail();
            } catch (NullPointerException expected) {
            }
            List<Integer> nullNotFirst = Lists.newArrayList(0, null);
            try {
                Range.encloseAll(nullNotFirst);
                fail();
            } catch (NullPointerException expected) {
            }
        }
    
        public void testEquivalentFactories() {
            new EqualsTester()
                    .addEqualityGroup(Range.all())
                    .addEqualityGroup(Range.atLeast(1), Range.downTo(1, CLOSED))
                    .addEqualityGroup(Range.greaterThan(1), Range.downTo(1, OPEN))
                    .addEqualityGroup(Range.atMost(7), Range.upTo(7, CLOSED))
                    .addEqualityGroup(Range.lessThan(7), Range.upTo(7, OPEN))
                    .addEqualityGroup(Range.open(1, 7), Range.range(1, OPEN, 7, OPEN))
                    .addEqualityGroup(Range.openClosed(1, 7), Range.range(1, OPEN, 7, CLOSED))
                    .addEqualityGroup(Range.closed(1, 7), Range.range(1, CLOSED, 7, CLOSED))
                    .addEqualityGroup(Range.closedOpen(1, 7), Range.range(1, CLOSED, 7, OPEN))
                    .testEquals();
        }
    }

本文参考:
RangesExplained
RangeTest

阅读全文