【发布时间】:2016-07-18 14:35:13
【问题描述】:
我有一个程序,它的核心是处理间隔的排序列表(一个中等大小的database 和一个大的queries 列表),两个列表都是排序的。每个数据库间隔都应该与所有重叠的查询匹配,然后查询列表将按照与读入相同的顺序写出。
这类似于扫描线系列算法(如果可能,请在这里用更好的措辞纠正我)。
为了让程序以非常大的输入运行,我想(1)尽可能“本地”工作,(2)尽快写出数据(也就是说,如果不再需要查询,它应该写出来)。
整个任务实现起来有点笨拙,但 MWE 看起来有点像底部给出的。实际上,数据库并没有那么大,可以将其加载到内存中以形成一个区间树左右。但是,处理查询的问题仍然存在。
我现在的问题是:是否有一个优雅的解决方案使用 Java 8 流,以便我可以从并行性中受益(处理具有多个查询的数据库有点昂贵)?
我意识到一个挑战是将每个查询记录与多个数据库记录分组。另一个挑战是在查询完成后立即对结果进行本地合并,并且未来的任何人都不能干扰下一个要写出的结果。
谢谢!
package mwe;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
class MWE {
// Half-open interval [begin, end)
public static class Interval {
String name;
int begin;
int end;
Interval(String name, int begin, int end) {
this.name = name;
this.begin = begin;
this.end = end;
}
boolean overlaps(Interval that) {
return (that.begin < this.end) && (this.begin < that.end);
}
@Override
public String toString() {
return "Interval [name=" + name + ", begin=" + begin + ", end=" + end + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + begin;
result = prime * result + end;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Interval other = (Interval) obj;
if (begin != other.begin)
return false;
if (end != other.end)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
// One counter for an interval
static class IntervalCounter {
int counter;
Interval itv;
IntervalCounter(Interval itv) {
this.counter = 0;
this.itv = itv;
}
@Override
public String toString() {
return "IntervalCounter [counter=" + counter + ", itv=" + itv + "]";
}
}
// DB intervals to come, sorted by begin position
static List<Interval> inactiveIntervals = new ArrayList<>();
// Currently active DB intervals, sorted by begin position
static List<Interval> activeIntervals = new ArrayList<>();
// Mapping from database to query interval
static HashMap<Interval, ArrayList<Interval>> dbToQueries = new HashMap<>();
// Mapping from interval to point into list of outgoing intervals
static HashMap<Interval, IntervalCounter> itvToCounter = new HashMap<>();
// List of outgoing qry intervals
static ArrayList<IntervalCounter> outgoingIntervals = new ArrayList<>();
static void process(List<Interval> db, List<Interval> qry) {
inactiveIntervals.addAll(db); // put all into queue
// Process each query interval
for (Interval q : qry) {
assignToIntervals(q);
processDone(q);
}
assignToIntervals(null);
processDone(null);
}
/**
* Given the current Interval q, process all database intervals for which no more overlap can come
*/
private static void processDone(Interval q) {
// Count number of database intervals that are done when q has been processed completely
int popCount = 0; // number of intervals to pop from front
for (Interval db : activeIntervals) {
if (q == null || q.begin >= db.end) {
System.err.println("Processing in DB " + db.name);
for (Interval itv : dbToQueries.get(db))
System.err.println(" " + itv.name);
popCount += 1;
} else {
break; // cannot guarantee done for next
}
}
// Remove them from the DB list and reduce counters of contained queries
while (popCount > 0) {
System.err.println("popping " + activeIntervals.get(0).name);
final Interval db = activeIntervals.get(0);
for (IntervalCounter counter : outgoingIntervals) {
if (counter.itv.overlaps(db))
counter.counter -= 1;
}
dbToQueries.remove(db);
activeIntervals.remove(0);
popCount--;
}
// Write out all queries that are marked as done
while (!outgoingIntervals.isEmpty() && outgoingIntervals.get(0).counter == 0) {
System.err.println("Writing out query " + outgoingIntervals.get(0).itv.name);
outgoingIntervals.remove(0);
}
}
private static void assignToIntervals(Interval q) {
// Activate new DB intervals
int popCount = 0;
for (Interval db : inactiveIntervals) {
if (q == null || q.end > db.begin) { // could overlap
activeIntervals.add(db);
dbToQueries.put(db, new ArrayList<>());
if (q != null) {
outgoingIntervals.add(new IntervalCounter(q));
itvToCounter.put(q, outgoingIntervals.get(outgoingIntervals.size() - 1));
}
popCount++;
} else {
break; // cannot pull in more
}
}
// Activate intervals
while (popCount > 0) {
inactiveIntervals.remove(0);
popCount--;
}
// Assign to active DB intervals
if (q == null)
return;
for (Interval db : activeIntervals) {
if (q.overlaps(db)) {
dbToQueries.get(db).add(q);
itvToCounter.get(q).counter += 1;
}
}
}
public static void main(String[] args) throws java.lang.Exception {
ArrayList<Interval> db = new ArrayList<>();
db.add(new Interval("db1", 1, 100));
db.add(new Interval("db2", 95, 190));
db.add(new Interval("db3", 200, 300));
ArrayList<Interval> qry = new ArrayList<>();
qry.add(new Interval("q1", 1, 20));
qry.add(new Interval("q2", 99, 100));
qry.add(new Interval("q3", 250, 251));
// Guarantee: db and qry will always be sorted by begin
process(db, qry);
}
}
上面程序运行时的输出如下
Processing in DB db1
q1
q2
Processing in DB db2
q2
popping db1
popping db2
Writing out query q1
Writing out query q2
Processing in DB db3
q3
popping db3
Writing out query q3
【问题讨论】:
标签: java algorithm java-stream