我在未排序的集合中有一组分层项目。每个项目都有一个字段previousItem:
public class Item{
public Item previousItem;
}
处理这些项目的最有效方法是什么,以便输出是一个集合,其中没有任何previousItem的Item是集合中的第一个Item,每个后续项目的previousItem是集合中的上一个项目?
我的第一个想法是在Item类中实现Comparable接口:
public int compareTo(Item that) {
final int BEFORE = -1;
final int EQUAL = 0;
final int AFTER = 1;
if(this.previousItem==null){
return BEFORE;
}
if(that.previousItem==null){
return AFTER;
}
if(this.previousItem.equals(that){
return AFTER;
}else if(that.previousItem.equals(this){
return BEFORE;
}
return EQUAL;
}
然后遍历项目并将它们添加到TreeSet:
SortedSet<Item> itemSortedSet = new TreeSet<Item>();
for (Item item : itemCollection) {
itemSortedSet.add(item);
}
是否有一种更有效的方式(处理所需的时间更少/需要的迭代次数)来对集合进行排序,使它们处于逻辑层次顺序?
答案 0 :(得分:6)
你的比较器不起作用:它不提供传递性,即如果在A-> B-> C的情况下比较项目A和C,它将做错误的事。
如果没有项目可以包含相同上一项,则Item
对象基本上构成基本链接列表。如果您碰巧知道哪一个是最后一项,您可以从那里开始一个循环并解开整个结构:
Item last = ...;
while (last != null) {
result.add(last)
last = last.previousItem
}
如果您无法找到最后一个项目,那么您可以使用IdentityHashMap
将每个previousItem
值映射到使用它的Item
对象:
IdentityHashMap<Item,Item> m = new IdentityHashMap<Item,Item>(itemset.size());
for (Item i : itemset)
m.put(i.previousItem, i);
Item i = m.get(null);
while (i != null) {
result.add(i);
i = m.get(i);
}
这将从没有上一个节点的项开始解析未分类的集合。
这两种方法都具有大致线性的复杂度w.r.t。项目数量,但他们做出两个假设,在您的情况下可能无效:
每个项目只能是最多一个其他节点的前一项,即您的项目是列表而不是树。
项目中只有一个“线程”。
如果这些假设中的任何一个无效,您将需要一个更复杂的算法来对其进行排序。
答案 1 :(得分:0)
从我看到的,你的Item已经实现了一个链表(如果不是通过名字),所以将它放入一个集合的自然(对我来说)的方式是将它转换为一个真实的列表递归。转换项目后,您可能需要根据您的规范撤消列表。
public class Item {
Item previousItem;
public void putToCollection(ArrayList<Item> list) {
list.add(this);
if (previousItem == null) {
Collections.reverse(list);
} else {
previousItem.putToCollection(list);
}
}
}
答案 2 :(得分:0)
如果我理解正确,你就可以在Item对象上找到没有前一个对象的句柄。您可以循环遍历Items(例如,使用while循环)并将它们添加到ArrayList或LinkedList,因为add() - 这些List实现的方法只是在列表的末尾附加一个新Item。这样,你就可以在O(n)-time中完成它。
答案 3 :(得分:0)
您可以使用最初放置所有项目的HashSet<Item>
。然后,您循环浏览项目并从集合中删除每个previousItem
。最后,您应该在集合中留下一个元素,最后一个元素,对于任何其他项目,它不是previousItem
。然后,您只需添加从该项开始并通过链接的项目。