【问题标题】:How to access the reference values of a HashSet<TValue> without enumeration?如何在不枚举的情况下访问 HashSet<TValue> 的参考值?
【发布时间】:2011-11-09 13:34:27
【问题描述】:

我有这种情况,其中内存保护是最重要的。我正在尝试将 > 1 GB 的肽序列读入内存并将共享相同序列的肽实例组合在一起。我将 Peptide 对象存储在 Hash 中,以便快速检查是否存在重复,但发现您无法访问 Set 中的对象,即使知道 Set 包含该对象也是如此。

内存真的很重要,如果可能的话,我不想复制数据。 (否则我会将我的数据结构设计为:peptides = Dictionary&lt;string, Peptide&gt; 但这会在字典和 Peptide 类中复制字符串)。以下是向您展示我想要完成的代码:

public SomeClass {

       // Main Storage of all the Peptide instances, class provided below
       private HashSet<Peptide> peptides = new HashSet<Peptide>();

       public void SomeMethod(IEnumerable<string> files) {
            foreach(string file in files) {
                 using(PeptideReader reader = new PeptideReader(file)) {
                     foreach(DataLine line in reader.ReadNextLine()) {
                         Peptide testPep = new Peptide(line.Sequence);
                         if(peptides.Contains(testPep)) {

                            // ** Problem Is Here **
                            // I want to get the Peptide object that is in HashSet
                            // so I can add the DataLine to it, I don't want use the
                            // testPep object (even though they are considered "equal")
                            peptides[testPep].Add(line); // I know this doesn't work

                            testPep.Add(line) // THIS IS NO GOOD, since it won't be saved in the HashSet which i use in other methods.

                         } else {
                            // The HashSet doesn't contain this peptide, so we can just add it
                            testPep.Add(line);
                            peptides.Add(testPep);
                         }
                     }   
                 }
            }
       }
}

public Peptide : IEquatable<Peptide> {
     public string Sequence {get;private set;}
     private int hCode = 0;

     public PsmList PSMs {get;set;}

     public Peptide(string sequence) {
         Sequence = sequence.Replace('I', 'L');
         hCode = Sequence.GetHashCode();             
     }

     public void Add(DataLine data) {
         if(PSMs == null) {
             PSMs = new PsmList();
         } 
         PSMs.Add(data);
     }

     public override int GethashCode() {
         return hCode;
     }

     public bool Equals(Peptide other) {
         return Sequence.Equals(other.Sequence);
     }
}

public PSMlist : List<DataLine> { // and some other stuff that is not important }

为什么HashSet 不让我获取包含在 HashSet 中的对象引用?我知道人们会说如果HashSet.Contains() 返回true,那么你的对象是等价的。它们在值方面可能是等价的,但我需要引用相同,因为我将附加信息存储在 Peptide 类中。

我想出的唯一解决方案是Dictionary&lt;Peptide, Peptide&gt;,其中键和值都指向同一个引用。但这似乎很俗气。是否有其他数据结构可以完成此任务?

【问题讨论】:

  • 非常有趣,但也是 Why can't I retrieve an item from a HashSet without enumeration? 的副本@ +1 仍然是一个很好的问题。
  • 当键也是值的属性时,扩展 KeyedCollection&lt;TKey, TItem&gt; 是一个不错的选择。
  • 我会在 Skeet 回复中添加一些内容:如果你想重新实现 HashSet,你可以从 mono 项目中获取并修改它:-) 你甚至可以尝试查看 C5 (itu.dk/research/c5 ) 看看有没有什么有用的。

标签: c# dictionary hashset


【解决方案1】:

使用Dictionary&lt;string, Peptide&gt;

【讨论】:

    【解决方案2】:

    基本上你可以自己重新实现HashSet&lt;T&gt;,但这是我所知道的唯一解决方案。 Dictionary&lt;Peptide, Peptide&gt;Dictionary&lt;string, Peptide&gt; 解决方案可能并没有那么低效 - 如果您只浪费每个条目的单个引用,我想这将是相对微不足道的。

    事实上,如果你从Peptide 中删除hCode 成员,这将为每个对象提供4 个字节的安全,这与x86 中的引用大小相同......到目前为止缓存散列没有意义我可以告诉你,你只会计算每个对象的哈希一次,至少在你展示的代码中。

    如果您真的非常需要记忆,我怀疑您可以比string 更有效地存储序列。如果您向我们提供有关序列包含内容的更多信息,我们或许可以在那里提出一些建议。

    我不知道为什么HashSet 不允许这样做有什么特别强烈的原因,除了这是一个相对罕见的要求 - 但这是我在 Java 中看到的要求还有……

    【讨论】:

    • 感谢您的快速回复。填充后,我在许多其他方法中使用 HashSet,因此我认为通过存储 HashCode 可以节省时间,因为它永远不会改变。这很容易修改。我考虑过以某种方式对序列进行编码(只有 20 个可能的字符),但觉得编码/解码的开销会损害占空比。但我想你不能总是一边吃蛋糕一边吃。
    • @Moop:哈希码只会被HashSet请求一次。如果您将同一个对象放在许多哈希集中,那可能会影响事情。至于排序 - 只存储一个字节数组而不是一个字符串会为你节省很多。不需要任何复杂的东西。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-01
    • 2020-02-25
    • 1970-01-01
    • 2022-12-05
    相关资源
    最近更新 更多