【问题标题】:Java: Retrieving an element from a HashSetJava:从 HashSet 中检索元素
【发布时间】:2012-08-18 00:07:51
【问题描述】:

希望有人能解释为什么我无法从HashSet 中检索元素。

考虑一下我的HashSet 包含一个MyHashObjects 列表,其中hashCode()equals() 方法被正确覆盖。

我希望自己构建一个MyHashObject,并将相关的哈希码属性设置为某些值。 我可以使用contains() 方法查询HashSet 以查看集合中是否存在“等效”对象。 因此,即使 contains() 为 2 个对象返回 true,它们也可能不是 == true。

为什么没有类似于contains() 工作方式的get() 方法?

有兴趣了解此 API 决策背后的想法

【问题讨论】:

    标签: java


    【解决方案1】:

    您可以HashMap<MyHashObject,MyHashObject> 代替HashSet<MyHashObject>

    在你的“重构”MyHashObject 上调用ContainsKey() 将首先hashCode()-检查集合,如果命中重复的哈希码,最后equals()-检查你的“重构”与原始,你可以使用get()检索原件

    这是 O(1),但缺点是您可能必须同时覆盖 equals()hashCode() 方法。

    【讨论】:

      【解决方案2】:

      如果您知道要检索的元素,那么您已经拥有该元素。给定一个元素,Set 要回答的唯一问题是它是否是 contains()

      如果您想对元素进行迭代,只需使用 Set.iterator()

      听起来你想要做的是为元素的等价类指定一个规范元素。您可以使用Map<MyObject,MyObject> 来执行此操作。请参阅this SO questionthis one 进行讨论。

      如果您真的确定要找到一个元素 .equals() 是您的原始元素,并且您必须使用 HashSet 的约束,我认为您会坚持迭代它并自己检查 equals()。 API 不允许您通过其哈希码获取某些内容。所以你可以这样做:

      MyObject findIfPresent(MyObject source, HashSet<MyObject> set)
      {
         if (set.contains(source)) {
            for (MyObject obj : set) {
              if (obj.equals(source)) 
                return obj;
            } 
         }
      
        return null;
      }
      

      蛮力和 O(n) 丑陋,但如果这是你需要做的......

      【讨论】:

      • 但这不是我想要的元素。我想要集合中的元素,而不是我拥有的与 hashCode 等效的元素。这有意义吗?
      • 不,如果有 3 个不同的对象具有相同的哈希码怎么办?你想要这些对象中的哪一个?
      • 这是一个哈希集。我假设只有一个对象会存在于具有该哈希码的 HashSet 中?
      • 建议您使用内部人员,如下所述:stackoverflow.com/a/3008667/83695
      • contains(Object): “当且仅当这个集合包含一个元素 e 使得 (o==null ? e==null : o.equals(e)) 时返回 true”。因此,hashCode 只是更快找到对象的帮手。
      【解决方案3】:

      首先将您的集合转换为数组。 然后,通过数组的索引获取项目。

      Set uniqueItem = new HashSet() ;
      uniqueItem.add("0");
      uniqueItem.add("1");
      uniqueItem.add("0");
      
      Object[] arrayItem = uniqueItem.toArray(); 
      for(int i = 0; i < uniqueItem.size();i++){
          System.out.println("Item "+i+" "+arrayItem[i].toString());
      }
      

      【讨论】:

        【解决方案4】:

        听起来您实际上是在尝试将哈希码用作映射中的键(这是 HashSet 在幕后所做的)。您可以通过声明 HashMap&lt;Integer, MyHashObject&gt; 来明确地做到这一点。

        HashSet 没有 get,因为通常您将作为参数提供给 get 方法的对象与您返回的对象相同。

        【讨论】:

        • 我认为 MVM 的答案更好(尽管它的票数更少)。不同的是,如果在 HashMap 中使用哈希码作为索引,如果发生冲突(当两个 MyHashObject 具有相同的哈希码时)会丢失数据。如果使用 HashMap,两个具有相同哈希码的对象可能共享一个桶,但不会发生冲突。
        • 如果有人想对 HashSets 使用 get 方法,则hashcodeequals 方法可能不会使用 ALL 的属性对象来计算和处理结果。可能剩余的属性包含一些其他值。
        【解决方案5】:

        最简单的方法之一是转换为数组:

        for(int i = 0; i < set.size(); i++) {
            System.out.println(set.toArray()[i]);
        }
        

        【讨论】:

          【解决方案6】:

          如果您知道 Set 中元素的顺序,则可以通过将 Set 转换为 Array 来检索它们。像这样的:

          Set mySet = MyStorageObject.getMyStringSet();
          Object[] myArr = mySet.toArray();
          String value1 = myArr[0].toString();
          String value2 = myArr[1].toString();
          

          【讨论】:

            【解决方案7】:

            您需要获取对包含在 Set 对象中的对象的引用的想法很常见。它可以通过两种方式归档:

            1. 根据需要使用 HashSet,然后:

              public Object getObjectReference(HashSet<Xobject> set, Xobject obj) {
                  if (set.contains(obj)) {
                      for (Xobject o : set) {
                          if (obj.equals(o))
                              return o;
                      }
                  }
                  return null;
              }
              

            要使这种方法起作用,您需要同时覆盖 hashCode() 和 equals(Object o) 方法 在最坏的情况下,我们有 O(n)

            1. 第二种方法是使用TreeSet

              public Object getObjectReference(TreeSet<Xobject> set, Xobject obj) {
                  if (set.contains(obj)) {
                      return set.floor(obj);
                  }
                  return null;
              }
              

            这种方法给 O(log(n)),效率更高。 对于这种方法,您不需要覆盖 hashCode,但您必须实现 Comparable 接口。 (定义函数 compareTo(Object o))。

            【讨论】:

              【解决方案8】:

              如果我在我的应用程序中确定该对象未在任何列表或哈希数据结构中用于搜索,并且未在其他地方使用 equals 方法,但添加时间接用于哈希数据结构的方法除外。是否建议在 equals 方法中更新集合中的现有对象。请参考以下代码。如果我将这个 bean 添加到 HashSet,我可以对键(id)上的匹配对象进行组聚合。通过这种方式,我也可以实现 sum、max、min 等聚合函数。如果不建议,请随时与我分享您的想法。

              public class MyBean {
              
                  String id,
                         name;
                  double amountSpent;
              
                  @Override
                  public int hashCode() {
                      return id.hashCode();
                  }
              
                  @Override
                  public boolean equals(Object obj) {
                      if(obj!=null && obj instanceof MyBean ) {
                          MyBean tmpObj = (MyBean) obj;
                          if(tmpObj.id!=null && tmpObj.id.equals(this.id)) {
                              tmpObj.amountSpent += this.amountSpent;
                              return true;
                          }
                      }
                      return false;
                  }
              }
              

              【讨论】:

                【解决方案9】:

                如果您可以使用List作为数据结构来存储您的数据,而不是使用Map将结果存储在Map的值中,您可以使用以下sn-p并将结果存储在同一个对象。

                这是一个Node类:

                private class Node {
                    public int row, col, distance;
                
                    public Node(int row, int col, int distance) {
                        this.row = row;
                        this.col = col;
                        this.distance = distance;
                    }
                
                    public boolean equals(Object o) {
                        return (o instanceof Node &&
                                row == ((Node) o).row &&
                                col == ((Node) o).col);
                    }
                }
                

                如果您将结果存储在 distance 变量中,并根据它们的坐标检查列表中的项目,您可以在 的帮助下使用以下方法将距离更改为新的>lastIndexOf 方法,只要您只需要为每个数据存储一个元素即可:

                    List<Node> nodeList;
                    nodeList = new ArrayList<>(Arrays.asList(new Node(1, 2, 1), new Node(3, 4, 5)));
                    Node tempNode = new Node(1, 2, 10);
                    if(nodeList.contains(tempNode))
                        nodeList.get(nodeList.lastIndexOf(tempNode)).distance += tempNode.distance;
                

                它基本上是重新实现Set,其项目可以访问和更改。

                【讨论】:

                  猜你喜欢
                  • 2016-04-15
                  • 1970-01-01
                  • 2011-04-19
                  • 2015-03-29
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2013-04-14
                  • 1970-01-01
                  相关资源
                  最近更新 更多