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
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。