我正在寻找基于Java的数据结构,该结构管理基于时间/日期的收集间隔(最好是Joda时间),这样对于添加到集合的每个间隔,数据结构返回所添加的子间隔(s)间隔,尚未在数据结构中并巩固间隔。
现在就集合理论而言,这将是容易的,即返回值将是“待添加”\“现有”,并且所得到的结构将是“待加”的“现有”联合。当然,我当然可以使用一组离散点来模拟日期/时间间隔,但这似乎并不真正有效。
所以我正在寻找现有的数据结构,它已经开箱即用使用间隔提供了这些设置操作。
只是为了澄清,这里是我正在寻找的例证。
// case 0 // existing ********************************************* // to be added ******** // return value --empty-- // result ********************************************* // // case 1 // existing ***************************************** // to be added ************ // return value **** // result ********************************************* // // case 2 // existing *************************************** // to be added **** // return value **** // result **** *************************************** // // case 3 // existing ***************************************** // to be added ************ // return value **** // result ********************************************* // // case 4 // existing *************************************** // to be added **** // return value **** // result *************************************** **** // // case 5 // existing ***************** *************** // to be added **** // return value **** // result ***************** **** *************** // // case 6 // existing ***************** *************** // to be added ******** // return value ***** // result ********************** *************** // // case 7 // existing ***************** *************** // to be added ******** // return value ***** // result ***************** ******************** // // case 8 // existing ***************** **** *************** // to be added ***************** // return value **** ***** // result ********************************************* // // ...... //
答案 0 :(得分:3)
没有现成的结构,至少在stdlib或commons中没有,但你可以使用NavigableMap
(TreeMap
)相对容易地实现某些功能。特别是floorEntry()
/ ceilingEntry()
和subMap()
等方法应该对此有用。
在我们的应用程序中,我们有一个范围聚合器类,用于计算范围的分配总和。即基本输入是一组(可能重叠)日期范围,其中附有数字。输出是相应的非重叠范围的集合,输入的数字在输入范围重叠的位置相加。它少于100行代码并使用TreeMap
。
答案 1 :(得分:1)
Guava Ranges可能会派上用场。他们已经描述了范围/间隔,其中一些实用方法有助于创建数据结构以维持这些间隔,如问题中所述。
事实上,它们比这里所需的间隔要强大得多,假设 all 的间隔是closed intervals,其中包含端点。但我猜这里没有理由重新发明轮子。
所需数据结构所需的操作是
Range#span
方法Range#isConnected
和Range#encloses
方法提供的。 最后一个是Ranges
类提供的不 - 原因很简单:两个范围之间的差异可能不是单个范围,但零,一个或 两个 范围。但它可以很容易地使用效用函数构建,它返回List
范围,包含0 ... 2个元素,具体取决于重叠配置。
以下是所需数据结构的可能实现:
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import com.google.common.collect.Range;
class Ranges
{
// Computes the result of subtracting all the given ranges from
// the given range.
static <T extends Comparable<? super T>> List<Range<T>> subtractAll(
Range<T> r, List<Range<T>> ranges)
{
List<Range<T>> currentList = Collections.singletonList(r);
for (Range<T> other : ranges)
{
List<Range<T>> nextList = new ArrayList<Range<T>>();
for (int i=0; i<currentList.size(); i++)
{
Range<T> current = currentList.get(i);
List<Range<T>> differences = subtract(current, other);
nextList.addAll(differences);
}
currentList = nextList;
}
return currentList;
}
// Computes the result of subtracting range r1 from
// the given range r0. The result will be
// - an empty list if r1 encloses r0
// - an two-element list if r0 encloses r1
// - a list containing r0 if the ranges are disjoint
// - a single-element list otherwise
static <T extends Comparable<? super T>> List<Range<T>> subtract(
Range<T> r0, Range<T> r1)
{
T min0 = r0.lowerEndpoint();
T max0 = r0.upperEndpoint();
T min1 = r1.lowerEndpoint();
T max1 = r1.upperEndpoint();
if (r0.encloses(r1))
{
List<Range<T>> result =
new ArrayList<Range<T>>();
result.add(Range.closed(min0, min1));
result.add(Range.closed(max1, max0));
return result;
}
else if (r1.encloses(r0))
{
return Collections.emptyList();
}
else if (r0.isConnected(r1))
{
T min = null;
T max = null;
if (min0.compareTo(min1) < 0)
{
min = min0;
max = min1;
}
else
{
min = max1;
max = max0;
}
return Collections.singletonList(Range.closed(min, max));
}
return Collections.singletonList(r0);
}
// "Normalize" the given list of ranges: As long as two of
// them are connected, they are replaced by their "span" (union)
static <T extends Comparable<? super T>> List<Range<T>> normalize(
List<Range<T>> ranges)
{
List<Range<T>> currentList = new ArrayList<Range<T>>(ranges);
while (true)
{
List<Range<T>> toRemove = new ArrayList<Range<T>>();
List<Range<T>> toAdd = new ArrayList<Range<T>>();
for (int i=0; i<currentList.size(); i++)
{
for (int j=i+1; j<currentList.size(); j++)
{
Range<T> r0 = currentList.get(i);
Range<T> r1 = currentList.get(j);
if (r0.isConnected(r1))
{
toRemove.add(r0);
toRemove.add(r1);
toAdd.add(r0.span(r1));
}
}
}
if (toAdd.isEmpty())
{
break;
}
Set<Range<T>> set = new LinkedHashSet<Range<T>>();
set.addAll(currentList);
set.removeAll(toRemove);
set.addAll(toAdd);
currentList = new ArrayList<Range<T>>(set);
}
return currentList;
}
}
class RangesContainer<T extends Comparable<? super T>>
{
private List<Range<T>> ranges;
RangesContainer()
{
ranges = new ArrayList<Range<T>>();
}
public List<Range<T>> add(Range<T> newRange)
{
if (ranges.stream().anyMatch(r -> r.encloses(newRange)))
{
return Collections.emptyList();
}
if (ranges.stream().noneMatch(r -> r.isConnected(newRange)))
{
ranges.add(newRange);
ranges = Ranges.normalize(ranges);
return Collections.singletonList(newRange);
}
List<Range<T>> differences = Ranges.subtractAll(newRange, ranges);
ranges.add(newRange);
ranges = Ranges.normalize(ranges);
return differences;
}
List<Range<T>> getRanges()
{
return Collections.unmodifiableList(ranges);
}
}
public class RangesTest
{
static final LocalDateTime ZERO =
LocalDateTime.of(2015, 1, 1, 0, 0);
public static void main(String[] args)
{
testCase0();
testCase1();
testCase2();
testCase3();
testCase4();
testCase5();
testCase6();
testCase7();
testCase8();
}
private static void testCase0()
{
System.out.println("Test case 0");
RangesContainer<LocalDateTime> rc =
new RangesContainer<LocalDateTime>();
Range<LocalDateTime> existing0 = Range.closed(
ZERO.plusMinutes(0),
ZERO.plusMinutes(45));
rc.add(existing0);
Range<LocalDateTime> added = Range.closed(
ZERO.plusMinutes(16),
ZERO.plusMinutes(24));
System.out.println("Existing:");
print(rc.getRanges());
System.out.println("Adding:");
print("", added);
List<Range<LocalDateTime>> returnValue = rc.add(added);
System.out.println("Returned:");
print(returnValue);
System.out.println("Result");
print(rc.getRanges());
System.out.println("\n");
}
private static void testCase1()
{
System.out.println("Test case 1");
RangesContainer<LocalDateTime> rc =
new RangesContainer<LocalDateTime>();
Range<LocalDateTime> existing0 = Range.closed(
ZERO.plusMinutes(4),
ZERO.plusMinutes(45));
rc.add(existing0);
Range<LocalDateTime> added = Range.closed(
ZERO.plusMinutes(0),
ZERO.plusMinutes(12));
System.out.println("Existing:");
print(rc.getRanges());
System.out.println("Adding:");
print("", added);
List<Range<LocalDateTime>> returnValue = rc.add(added);
System.out.println("Returned:");
print(returnValue);
System.out.println("Result");
print(rc.getRanges());
System.out.println("\n");
}
private static void testCase2()
{
System.out.println("Test case 2");
RangesContainer<LocalDateTime> rc =
new RangesContainer<LocalDateTime>();
Range<LocalDateTime> existing0 = Range.closed(
ZERO.plusMinutes(6),
ZERO.plusMinutes(45));
rc.add(existing0);
Range<LocalDateTime> added = Range.closed(
ZERO.plusMinutes(0),
ZERO.plusMinutes(4));
System.out.println("Existing:");
print(rc.getRanges());
System.out.println("Adding:");
print("", added);
List<Range<LocalDateTime>> returnValue = rc.add(added);
System.out.println("Returned:");
print(returnValue);
System.out.println("Result");
print(rc.getRanges());
System.out.println("\n");
}
private static void testCase3()
{
System.out.println("Test case 3");
RangesContainer<LocalDateTime> rc =
new RangesContainer<LocalDateTime>();
Range<LocalDateTime> existing0 = Range.closed(
ZERO.plusMinutes(0),
ZERO.plusMinutes(41));
rc.add(existing0);
Range<LocalDateTime> added = Range.closed(
ZERO.plusMinutes(33),
ZERO.plusMinutes(45));
System.out.println("Existing:");
print(rc.getRanges());
System.out.println("Adding:");
print("", added);
List<Range<LocalDateTime>> returnValue = rc.add(added);
System.out.println("Returned:");
print(returnValue);
System.out.println("Result");
print(rc.getRanges());
System.out.println("\n");
}
private static void testCase4()
{
System.out.println("Test case 4");
RangesContainer<LocalDateTime> rc =
new RangesContainer<LocalDateTime>();
Range<LocalDateTime> existing0 = Range.closed(
ZERO.plusMinutes(0),
ZERO.plusMinutes(39));
rc.add(existing0);
Range<LocalDateTime> added = Range.closed(
ZERO.plusMinutes(41),
ZERO.plusMinutes(45));
System.out.println("Existing:");
print(rc.getRanges());
System.out.println("Adding:");
print("", added);
List<Range<LocalDateTime>> returnValue = rc.add(added);
System.out.println("Returned:");
print(returnValue);
System.out.println("Result");
print(rc.getRanges());
System.out.println("\n");
}
private static void testCase5()
{
System.out.println("Test case 5");
RangesContainer<LocalDateTime> rc =
new RangesContainer<LocalDateTime>();
Range<LocalDateTime> existing0 = Range.closed(
ZERO.plusMinutes(0),
ZERO.plusMinutes(17));
Range<LocalDateTime> existing1 = Range.closed(
ZERO.plusMinutes(30),
ZERO.plusMinutes(45));
rc.add(existing0);
rc.add(existing1);
Range<LocalDateTime> added = Range.closed(
ZERO.plusMinutes(21),
ZERO.plusMinutes(25));
System.out.println("Existing:");
print(rc.getRanges());
System.out.println("Adding:");
print("", added);
List<Range<LocalDateTime>> returnValue = rc.add(added);
System.out.println("Returned:");
print(returnValue);
System.out.println("Result");
print(rc.getRanges());
System.out.println("\n");
}
private static void testCase6()
{
System.out.println("Test case 6");
RangesContainer<LocalDateTime> rc =
new RangesContainer<LocalDateTime>();
Range<LocalDateTime> existing0 = Range.closed(
ZERO.plusMinutes(0),
ZERO.plusMinutes(17));
Range<LocalDateTime> existing1 = Range.closed(
ZERO.plusMinutes(30),
ZERO.plusMinutes(45));
rc.add(existing0);
rc.add(existing1);
Range<LocalDateTime> added = Range.closed(
ZERO.plusMinutes(14),
ZERO.plusMinutes(22));
System.out.println("Existing:");
print(rc.getRanges());
System.out.println("Adding:");
print("", added);
List<Range<LocalDateTime>> returnValue = rc.add(added);
System.out.println("Returned:");
print(returnValue);
System.out.println("Result");
print(rc.getRanges());
System.out.println("\n");
}
private static void testCase7()
{
System.out.println("Test case 7");
RangesContainer<LocalDateTime> rc =
new RangesContainer<LocalDateTime>();
Range<LocalDateTime> existing0 = Range.closed(
ZERO.plusMinutes(0),
ZERO.plusMinutes(17));
Range<LocalDateTime> existing1 = Range.closed(
ZERO.plusMinutes(30),
ZERO.plusMinutes(45));
rc.add(existing0);
rc.add(existing1);
Range<LocalDateTime> added = Range.closed(
ZERO.plusMinutes(25),
ZERO.plusMinutes(33));
System.out.println("Existing:");
print(rc.getRanges());
System.out.println("Adding:");
print("", added);
List<Range<LocalDateTime>> returnValue = rc.add(added);
System.out.println("Returned:");
print(returnValue);
System.out.println("Result");
print(rc.getRanges());
System.out.println("\n");
}
private static void testCase8()
{
System.out.println("Test case 8");
RangesContainer<LocalDateTime> rc =
new RangesContainer<LocalDateTime>();
Range<LocalDateTime> existing0 = Range.closed(
ZERO.plusMinutes(0),
ZERO.plusMinutes(17));
Range<LocalDateTime> existing1 = Range.closed(
ZERO.plusMinutes(21),
ZERO.plusMinutes(25));
Range<LocalDateTime> existing2 = Range.closed(
ZERO.plusMinutes(30),
ZERO.plusMinutes(45));
rc.add(existing0);
rc.add(existing1);
rc.add(existing2);
Range<LocalDateTime> added = Range.closed(
ZERO.plusMinutes(15),
ZERO.plusMinutes(31));
System.out.println("Existing:");
print(rc.getRanges());
System.out.println("Adding:");
print("", added);
List<Range<LocalDateTime>> returnValue = rc.add(added);
System.out.println("Returned:");
print(returnValue);
System.out.println("Result");
print(rc.getRanges());
System.out.println("\n");
}
static void print(List<Range<LocalDateTime>> list)
{
for (int i=0; i<list.size(); i++)
{
Range<LocalDateTime> range = list.get(i);
String message = String.format("%3d", i);
print(message, range);
}
}
static void print(String message, LocalDateTime t0, LocalDateTime t1)
{
long minutes0 = ZERO.until(t0, ChronoUnit.MINUTES);
long minutes1 = ZERO.until(t1, ChronoUnit.MINUTES);
System.out.printf("%10s:", message);
for (long i=0; i<minutes0; i++)
{
System.out.print(" ");
}
for (long i=minutes0; i<minutes1; i++)
{
System.out.print("*");
}
System.out.println();
}
static void print(String message, Range<LocalDateTime> r)
{
print(message, r.lowerEndpoint(), r.upperEndpoint());
}
}
输出如下:
Test case 0
Existing:
0:*********************************************
Adding:
: ********
Returned:
Result
0:*********************************************
Test case 1
Existing:
0: *****************************************
Adding:
:************
Returned:
0:****
Result
0:*********************************************
Test case 2
Existing:
0: ***************************************
Adding:
:****
Returned:
0:****
Result
0: ***************************************
1:****
Test case 3
Existing:
0:*****************************************
Adding:
: ************
Returned:
0: ****
Result
0:*********************************************
Test case 4
Existing:
0:***************************************
Adding:
: ****
Returned:
0: ****
Result
0:***************************************
1: ****
Test case 5
Existing:
0:*****************
1: ***************
Adding:
: ****
Returned:
0: ****
Result
0:*****************
1: ***************
2: ****
Test case 6
Existing:
0:*****************
1: ***************
Adding:
: ********
Returned:
0: *****
Result
0: ***************
1:**********************
Test case 7
Existing:
0:*****************
1: ***************
Adding:
: ********
Returned:
0: *****
Result
0:*****************
1: ********************
Test case 8
Existing:
0:*****************
1: ****
2: ***************
Adding:
: ****************
Returned:
0: ****
1: *****
Result
0:*********************************************
我为间隔边框选择LocalDateTime
,但任何Comparable
对象都可以。对于测试,在那里打印的星数只是自任意ZERO
LocalDateTime
以来的分钟数。
尽管此处介绍了您所描述的测试用例,并提供了所需的结果,但可以/应该引入其他测试。例如,添加空间隔,或添加准确位于其他两个间隔边界的间隔...(这就是为什么这些被称为边界情况; - ))
我还应该提到,实施不一定是最有效或最优雅的。我在那里介绍的normalize
方法可能可以通过范围边界的勤勉簿记来避免,可能使用另一个答案中建议的NavigableSet
或NavigableMap
。如果性能至关重要,那么像Interval Trees这样的复杂数据结构也值得一看。但我认为给定的实施是合理的,相对容易理解。