文章目錄
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 <= c
,range.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
是一個包含值CLOSED
和OPEN
的枚舉。
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
公開了以下方法:
hasLowerBound()
和hasUpperBound()
,檢查區間是否具有指定的端點,或是無限的。lowerBoundType()
和upperBoundType()
返回對應端點的BoundType
,可以爲CLOSED
或OPEN
。如果此區間沒有指定的端點,則該方法將拋出IllegalStateException
。lowerEndpoint()
和upperEndpoint()
返回指定盡頭上的端點,如果區間沒有指定的端點,則拋出IllegalStateException
。isEmpty()
測試區間是否爲空,即有[a,a)
或(a,a]
的形式.
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
測試是否有某個區間包圍這兩個區間,但這等同於數學定義,即區間的並集必須形成一個連接集(空區間的特殊情況除外)。
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
來查看一個區間內的日期集,其JODADateTime
包含的時間不超過秒:因爲它不包含該類型的所有元素。 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