我需要使用自定义比较器订购一个集合而不在内存中复制它。
天真的实施将是:
Set<MyClass> newSet = new TreeSet<>(myComparator);
newSet.addAll(oldSet);
但这意味着,即使在有限的时间内,我也会在内存中有两个集合:oldSet(无序)和newSet(有序)。由于它们非常大,我想避免这种情况。
我想做这样的事情:
oldSet = new TreeSet<>(oldSet, myComparator);
实际上是不可能的,因为没有TreeSet的构造函数具有这样的结构。
这可能是一个解决方案吗?
Iterator<MyClass> it = oldSet.iterator();
Set<MyClass> newSet = new TreeSet<>(myComparator);
while(it.hasNext())
{
newSet.add(it.next());
it.remove();
}
更好的建议?
谢谢
答案 0 :(得分:2)
使用TreeSet
对内存效率最高,并不是最快的方法。
您应该使用ArrayList
并对其执行排序:
List<MyClass> sorted = new ArrayList<>(oldSet.size());
oldSet = null;
Collections.sort(sorted, myComparator);
ArrayList
中使用的单个数组的开销不应成为问题,无论如何,这是您可以拥有的最小问题。
单次批量排序操作比为TreeSet
中的每个单独项目找到正确的位置更快,以及在这种情况下所需的所有分配。
答案 1 :(得分:0)
由于Set
未按定义排序,因此无法订购Set
,因此(如果您这样做)您必须使用有序数据结构。但是,您根本不需要关心您看到的问题,如果执行Set
,Java将不会执行addAll
的深层副本,它只会复制几乎不使用RAM的引用。< / p>
所以你的addAll
解决方案是一个干净而正确的解决方案。
答案 2 :(得分:0)
如果你可以将所有对旧集合的引用置零,那就
newSet.addAll(oldSet);
oldSet = null;
如果你不能将所有对旧集的引用置零,请使用Set.clear方法
newSet.addAll(oldSet);
oldSet.clear();
请注意,在清除HashSet的内部哈希表后不收缩
答案 3 :(得分:0)
在构造函数中创建set时使用set,可以创建吞下副本。您只复制参考。删除时也会删除引用。它在以下代码中可见:
MyComparator myComparator = new MyComparator();
Set<Object> newSet = new TreeSet<>(myComparator);
Object mc = new Object();
newSet.add(mc); //set is created
Set<Object> newerSet = new TreeSet<>(myComparator);
newerSet.addAll(newSet);
System.out.println(newSet);
System.out.println(newerSet);
输出: [java.lang.Object@1bb1deea] [java.lang.Object@1bb1deea]
引用同一个对象。
newerSet.remove(mc);
System.out.println("After deletion");
System.out.println(newSet);
System.out.println(newerSet);
删除后 [java.lang.Object@1bb1deea] []
仅删除引用。
答案 4 :(得分:0)
您应该编写一个Iterator
实现,其中每次调用next()
都会为您提供下一个已排序的项目。它不会占用额外的内存,但与复制无序Set
相比,额外内存量会很小。您也没有新的Set
,但您可以遍历它。
低内存版本,但效率低下的算法会将最近访问过的项目存储在Iterator
中。每当您需要返回下一个项目时,您都会检查后备Set
中的所有项目,以确定下一个项目。