LinkedHashMap 与 LinkedHashSet 用于检索特定元素和按插入顺序检索

LinkedHashMap vs LinkedHashSet for retrieving specific elements & retrieving in insertion order

我正在做一个显然需要使用 Set 的问题,但我需要按插入顺序从集合中检索元素 + 检索特定元素。

但是找到特定元素太慢了(我想 O(n) 因为我必须遍历整个集合才能找到 & return)。

所以我选择了 LinkedHashMap<someClass,someClass>,其中键值映射包含相同的对象。

虽然速度更快,但它使用了两倍的内存,如果我的 key/value(两者都一样)碰巧占用了很多 space。

我希望如果有人对我的问题有更好的解决方案或优化。

编辑: 顺便说一句 SO answer 的评论可能会有所帮助

编辑:

 public Set<CompanyDummy> run(int numberOfCompanies) 
 {
        Set<CompanyDummy> companies=new LinkedHashSet<>();
        
        //Create companies
        CompanyDummy company;
        for(int idx=0;idx<=numberOfCompanies-1;idx++)
        {
            company=new CompanyDummy(idx);
            companies.add(company);
        }
        
        
        //Some code here to fill up each CompanyDummy element with engineers
        
        //At this point,there will be input 
        //specifying which companies to merge via their index(not reference)
        //Problem guarantees those companies exist. Hence, my reason why 
        //I didn't  do something like
        //if (set.contains(value)) return value;
        
        //Do note when we merge companies u & v, v is closed down
        
        for(int idx=0;idx<=transactions-1;idx++)
        {
            companyID= scanner.nextInt();
            anotherCompanyID= scanner.nextInt();
            
            //This part is where I search through companies to find if one exists
            //which is O(n)
            //Replacing sets with maps somehow makes the program faster
            //despite LinkedHashSet being backed by LinkedHashMap
            company=findCompany(companies, companyID);
            anotherCompany=findCompany(companies, anotherCompanyID);
            
            if(company!=null && anotherCompany!=null)
            {
                company.union(anotherCompany);
                companies.remove(anotherCompany);
            }
            

        }
 }
 
 private CompanyDummy findCompany(Set<CompanyDummy> companies,int id)
 {
        
        for(CompanyDummy company : companies)
        {
            if(company.getIndex()==id)
            {
                return company;
            }
        }
        
        return null;
  }

}


class CompanyDummy
{
private int index;
private Set<Integer> engineers;//The Integer here just denotes the engineer

public  CompanyDummy(int index) 
{
    this.index=index;
}

public int getindex()
{
    return index;
}

public void union(CompanyDummy someCompany)
{
    this.engineers.addAll(someCompany.engineers);
}

}

Although this was faster, it used up twice as much memory which is especially concerning if my key/value(both same anyways) happened to take up alot of space.

我看不出 LinkedHashMap 会更快。考虑到 LinkedHashSet 是使用后备 LinkedHashMap 实例实现的,可能会快一点,因此与直接使用 LinkedHashMap 相比,它会有一点开销。

至于内存,无论 key/value 实例的大小如何,两者都将占用完全相同的内存量。您会看到,当您将元素 X 添加到 LinkedHashSet 时,它实际上将条目 <X,PRESENT> 放入基础 LinkedHashMap(其中 PRESENT 是对某个虚拟 Object).使用 <X,X> 而不是 <X,PRESENT> 没有区别,因为 LinkedHashMap 只包含对键和值的引用,而不是 key/value 实例的副本。

But finding the specific element was too slow (I guess O(n) since I have to go through the entire set to find it)

A HashSet/LinkedHashSet 需要 O(1) 来确定特定元素是否已经在 Set 中。您不必遍历整个 Set。如果 Set 包含该元素,则您已经有了对它的引用。您不必寻找与您要搜索的元素相同的 Set 元素。

在java中,HashSet是使用HashMap实现的

HashSet 确实不适用于特定元素检索。映射中的键应该是标识映射到它的值对象的东西。此外,将复杂对象作为映射键通常不是一个好主意,除非你有一个非常好的 HashCode() 实现。

map 也不会维护插入顺序(LinkedHashMap 可能会保留它,因为它由 LinkedList 支持)。您可以将键设置为表示插入顺序的整数序列。例如:

Map<Integer, SomeClass> map = new HashMap<>();
map.put(0, object1);
map.put(1, object2);

等等。这将比 LinkedList 更好,因为获取时间是 O(1)。稍后,您可以在带有 运行 索引的 for 循环中获取它们,以按插入顺序获取它们。

如果您需要 HashSet 的 属性 来防止集合中的重复项,您将需要保留一组每个对象的一些唯一标识符。例如一个人的 ID#。在将每个对象插入地图之前,您还检查“唯一键”是否不在集合中,如果是,则将“唯一键”插入集合和地图。这完全取决于您的对象的性质。

如果您还有其他需要,请在评论中告诉我。祝你好运!

好的,现在我看到了代码,我可以很好地理解问题并给你一个解决方案。

您不仅仅是从集合中检索特定元素。如果元素是集合的成员,您实际上是在特定字段中检索具有特定值的元素。这就是为什么你不能使用集合(或映射)的快速查找操作1.

解决方案:

如果您想以比 O(N) 更好的速度进行检索,则需要 Map<Integer, CompanyDummy>。必须填充映射,以便它从 id 映射到 CompanyDummy,并将 id 作为其 index。然后您可以将 findCompany 调用替换为 Map.get 调用。

请注意,将 Set<CompanyDummy> 替换为 Map<CompanyDummy, CompanyDummy> 将无助于解决此问题。两者都会给你 O(N) 性能。


你说的是关于用 LinkedHashMap<CompanyDummy, CompanyDummy> 替换 LinkedHashSet<CompanyDummy> 的实验:

Although this was faster, it used up twice as much memory which is especially concerning if my key/value(both same anyways) happened to take up a lot of space.

我怀疑这两件事是否真的存在。

  1. Space 这两个数据结构的使用应该是相同的,因为 LinkedHashSet 实际上是 LinkedHashMap 在幕后。

  2. (IMO)不太可能存在显着的实际性能差异。当然不是 2 倍的差异。最可能的解释是,您用于测量和比较性能的方法确实适当考虑了由(各种)JVM 启动开销引起的可能的时序失真;例如class 加载、调整堆大小和垃圾收集以及 JIT 编译。


1 - 为了完整起见,如果不要求您的场景/地图按插入顺序排序,您可以使用 TreeSet<CompanyDummy> 和自定义 Comparatorindex 值对 CompanyDummy 个对象进行排序。然后,您可以使用 OrderedSet.ceiling 和虚拟 CompanyDummy 实例来探测 O(logN).

中的集合