【问题标题】:How to get nth element of a Set如何获取集合的第 n 个元素
【发布时间】:2014-08-19 07:10:31
【问题描述】:

更具体地说:如何获取 LinkedHashSet 的第 n 个元素(具有可预测的迭代顺序)?我想检索插入到此 Set 中的第 n 个元素(尚未出现)。

使用List 更好吗:

List<T> list = new ArrayList<T>(mySet);
T value = list.get(x); // x < mySet.size()

toArray(T [] a) 方法:

T [] array = mySet.toArray(new T[mySet.size()]);
T value = array[y]; // y < mySet.size()

除了(可能是轻微的)性能差异之外,还有什么需要注意的吗?有明确的赢家吗?

编辑 1

注意:为什么我想要最后插入的元素并不重要,重要的是我想要它。之所以选择 LinkedHashSet,是因为它“定义了迭代顺序,即元素插入集合的顺序(insertion-order)。请注意,如果将元素重新插入集合中,则插入顺序不受影响。”

编辑 2

这个问题似乎已经转移到讨论任何 Set 实现是否可以永远保留原始插入顺序。所以我在http://pastebin.com/KZJ3ETx9 上放了一些简单的测试代码来证明是的,LinkedHashSet 确实保留了插入顺序(与其迭代顺序相同),正如它的 Javadoc 所声称的那样。

编辑 3

修改了问题的描述,这样大家就不会太专注于检索Set 的最后一个元素(我原本以为问题的标题就足够暗示了——显然我错了)。

【问题讨论】:

  • 如果插入顺序很重要,您为什么首先使用Set?如果元素已经存在于集合中,那么“最后插入的元素”是什么意思?
  • @markvgti 没有所谓的“最新鲜”的集合...一个集合没有顺序!
  • @alfasin "这个链表定义了迭代顺序,这是元素插入集合的顺序(插入顺序)。" 迭代顺序广告订单。
  • @alfasin 这是一个集合。您不能将元素“重新插入”到其中,因为它不包含重复项。一旦一个元素在那里,它就在那里。 “重新插入”一个元素是没有意义的,因为它不会改变Set,也不应该改变其他任何东西。副作用太难处理了。
  • @alfasin 然而有一个集合实现 with 一个命令——那个实现是LinkedHashSetSet 不保证订购,也不禁止订购。因此,Set 的实现有订单是完全可以的。

标签: java collections set


【解决方案1】:

此方法基于更新后的要求返回第 n 个元素,而不仅仅是最后一个元素。如果来源是例如一个标识符为mySet的Set,最后一个元素可以被nthElement(mySet, mySet.size()-1)选中。

如果nSet 的大小相比较小,则此方法可能比例如转换为ArrayList

  /**
   * Return an element selected by position in iteration order.
   * @param data The source from which an element is to be selected
   * @param n The index of the required element. If it is not in the 
   * range of elements of the iterable, the method returns null.
   * @return The selected element.
   */
  public static final <T> T nthElement(Iterable<T> data, int n){
    int index = 0;
    for(T element : data){
      if(index == n){
        return element;
      }
      index++;
    }
    return null;
  }

【讨论】:

    【解决方案2】:

    如果你想检索最后一个元素,我会使用 LinkedHashSet 的迭代器:

    Iterator<T> it = linkedHashSet.iterator();
    T value = null;
    
    while (it.hasNext()) {
        value = it.next();
    }
    

    循环执行后的值将引用最后一个元素。

    【讨论】:

    • 你没有回答问题!他没有问“怎么做”,而是问哪种方式更有效
    • @alfasin 用我自己的解决方案回答,我隐含地声称这种方式更有效。
    • 它比什么更有效?它实际上比他提供的任何一个选项都更有效,两者都是 O(n),你的解决方案也是如此。
    • @alfasin 它具有相同的渐近行为,但它具有比其他解决方案更小的常数因子,因此显然它更有效。
    • 不,你没有。请阅读我的评论。由于 OP 建议的两种方法 完全 做了@Juvanis 所做的事情,再加上更多(分配对象和数组),因此您可以证明 Juvanis 解决方案所做的工作更少,这是“更高效”的一个很好的定义“。顺便说一句,您引入了大 O 符号,OP 从未提及它。
    【解决方案3】:

    所以我决定对@Juvanis 的回答稍作改动。

    获取 LinkedHashSet 中的第 n 个元素:

    Iterator<T> itr = mySet.iterator();
    int nth = y;
    T value = null;
    
    for(int i = 0; itr.hasNext(); i++) {
        value = itr.next();
        if (i == nth) {
            break;
        }
    }
    

    第二版代码:

    public class SetUtil {
    
        @Nullable
        public static <T> T nthElement(Set<T> set, int n) {
            if (null != set && n >= 0 && n < set.size()) {
                int count = 0;
                for (T element : set) {
                    if (n == count)
                        return element;
                    count++;
                }
            }
            return null;
        }
    }
    

    注意:稍作修改,上述方法可用于所有Iterables&lt;T&gt;

    这避免了确保SetList 保持同步的开销,并且还避免了每次都必须创建一个新的List(这将比任何数量的算法复杂性都更耗时)。

    显然我使用Set 来确保唯一性,我宁愿避免冗长解释为什么我需要索引访问。

    【讨论】:

      【解决方案4】:

      您可以使用以下解决方案, 这里我在 HashSet 中添加了 ModelClass 的对象。

      ModelClass m1 = null;
      int nth=scanner.nextInt();
      for(int index=0;index<hashset1.size();index++){
          m1 = (ModelClass) itr.next();
          if(nth == index) {
              System.out.println(m1);
              break;
          }
      }
      

      【讨论】:

        【解决方案5】:

        Set 是无序的,因此最后插入的元素的信息会丢失。因此,您不能插入最后一个元素。所以首先不要使用 Set,或者,如果你真的想跟踪最后一个元素,创建一个包含这样的类

        class mySetAndLast extends Set{
           T last;
           Set<T> mySet;       
        }
        

        现在的问题是“插入的最后一个元素”是什么。想象一下你的集合是空的

        -> insert x -> ok, x is the last inserted 
        -> insert y (y!=x) -> ok: y is the last inserted 
        -> insert x -> ? 
        

        现在是 x 还是 y 最后插入? x 没有被插入,因为 y 是最后插入的元素并且 x 已经是集合的一个元素,另一方面 x 从用户的角度来看是最后插入的..

        【讨论】:

        • 来自LinkedHashSet的javadoc:“定义迭代顺序,即元素插入集合的顺序(插入顺序)。请注意,如果元素插入顺序不受影响被重新插入到集合中。”我专门使用LinkedHashSet 是有原因的。
        • T 是从哪里来的?和命名约定? compareToequalshashCode 方法在哪里?扩展Set 是正确的做法吗?
        • 这在技术上甚至无法编译,因为Set 是一个接口。不过这个想法是对的。
        • 考虑到这是Set,您只需“插入”x一次。如果您尝试添加重复元素,它将无法添加,句号。这就是Set 的定义。 “用户的观点”是什么并不重要。尝试多次将某些内容放入常规HashSet 中也只会插入一次。 add() 方法有返回值是有原因的。
        • @HelpVampire666 如果我想要第 5 个元素或第 11 个元素,这个想法是行不通的。我询问的两种方法在所有情况下都有效。我只是在问哪个更好(以及为什么)。
        【解决方案6】:

        为了您自己的内部目的,您可以从任何 List 实现“破解”您自己的 Set

        public class ListSet<E> extends ArrayList<E> implements Set<E> {
            @Override
            public boolean add(E item) {
                return contains(item) ? false : super.add(item);
            }
        
            // ... and same for add(int, E), addAll(...), etc.
        }
        

        该示例很慢(添加的 O(n)),但由于您是实现它的人,您可以根据您的规范为contains() 使用更智能的代码返回它。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-12-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-10-16
          • 2017-03-11
          相关资源
          最近更新 更多