我最近不得不这样做,但项目可能存在多次。这很复杂,但我能够使用前瞻计数器和其他一些疯狂的方法来做到这一点。它看起来很像 Rob 的解决方案,所以感谢他让我开始!
首先,假设我们要返回将第一个列表转换为第二个列表的操作列表:
public interface Operation {
/**
* Apply the operation to the given list.
*/
void apply(List<String> keys);
}
我们有一些辅助方法来构造操作。您实际上不需要“移动”操作,甚至还可以进行“交换”(或替代),但这就是我所采用的:
Operation delete(int index) { ... }
Operation insert(int index, String key) { ... }
Operation move(int from, int to) { ... }
现在我们将定义一个特殊的类来保存我们的前瞻计数:
class Counter {
private Map<String, Integer> counts;
Counter(List<String> keys) {
counts = new HashMap<>();
for (String key : keys) {
if (counts.containsKey(key)) {
counts.put(key, counts.get(key) + 1);
} else {
counts.put(key, 1);
}
}
}
public int get(String key) {
if (!counts.containsKey(key)) {
return 0;
}
return counts.get(key);
}
public void dec(String key) {
counts.put(key, counts.get(key) - 1);
}
}
还有一个帮助方法来获取列表中下一个键的索引:
int next(List<String> list, int start, String key) {
for (int i = start; i < list.size(); i++) {
if (list.get(i).equals(key)) {
return i;
}
}
throw new RuntimeException("next index not found for " + key);
}
现在我们准备好进行转换了:
List<Operation> transform(List<String> from, List<String> to) {
List<Operation> operations = new ArrayList<>();
// make our own copy of the first, that we can mutate
from = new ArrayList<>(from);
// maintain lookahead counts
Counter fromCounts = new Counter(from);
Counter toCounts = new Counter(to);
// do all our deletes first
for (int i = 0; i < from.size(); i++) {
String current = from.get(i);
if (fromCounts.get(current) > toCounts.get(current)) {
Operation op = delete(i);
operations.add(op);
op.apply(from);
fromCounts.dec(current);
i--;
}
}
// then one more iteration for the inserts and moves
for (int i = 0; i < to.size(); i++) {
String current = to.get(i);
if (from.size() > i && from.get(i).equals(current)) {
fromCounts.dec(current);
continue;
}
if (fromCounts.get(current) > 0) {
Operation op = move(next(from, i + 1, current), i);
operations.add(op);
op.apply(from);
fromCounts.dec(current);
} else {
Operation op = insert(i, current);
operations.add(op);
op.apply(from);
}
}
return operations;
}
搞清楚你的头脑有点棘手,但基本上你会执行删除操作,这样你就知道你插入或移动的每个键。然后你再次浏览列表,如果有足够的,你从列表中你还没有看到的部分移动一个,否则插入。当你走到尽头时,一切都排好了。