简体   繁体   中英

How to get nth element of a Set

More specifically: how to get the nth element of a LinkedHashSet (which has a predictable iteration order)? I want to retrieve the nth element inserted into this Set ( which wasn't already present ).

Is it better to use a List :

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

or the toArray(T [] a) method:

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

Other than the (likely slight) performance differences, anything to watch out for? Any clear winner?

Edit 1

NB: It doesn't matter why I want the last-inserted element, all that matters is that I want it. LinkedHashSet was specifically chosen because it "defines the iteration ordering, which is the order in which elements were inserted into the set (insertion-order). Note that insertion order is not affected if an element is re-inserted into the set."

Edit 2

This question seems to have devolved into a discussion of whether any Set implementation can ever preserve original insertion order. So I put up some simple test code at http://pastebin.com/KZJ3ETx9 to show that yes, LinkedHashSet does indeed preserve insertion order (the same as its iteration order) as its Javadoc claims.

Edit 3

Modified the description of the problem so that everybody isn't too focused on retrieving the last element of the Set (I originally thought that the title of the question would be enough of a hint — obviously I was wrong).

This method is based on the updated requirement to return the nth element, rather than just the last element. If the source is eg a Set with identifier mySet , the last element can be selected by nthElement(mySet, mySet.size()-1) .

If n is small compared to the size of the Set , this method may be faster than eg converting to an 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;
  }

I'd use the iterator of the LinkedHashSet if you want to retrieve the last element:

Iterator<T> it = linkedHashSet.iterator();
T value = null;

while (it.hasNext()) {
    value = it.next();
}

After the loop execution value will be referring to the last element.

So I decided to go with a slight variation of the answer by @Juvanis.

To get at the nth element in a LinkedHashSet:

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;
    }
}

Version 2 of the code:

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;
    }
}

NB: with some slight modifications the method above can be used for all Iterables<T> .

This avoids the overhead of ensuring that a Set and a List stay in sync, and also avoids having to create a new List every time (which will be more time-consuming than any amount of algorithmic complexity).

Obviously I am using a Set to ensure uniqueness and I'd rather avoid a lengthy explanation as to why I need indexed access.

You can go with below solution, here i have added object of ModelClass in HashSet.

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;
    }
}

Set is unordered so the information on the last element inserted is lost. You cannot as such get the last element inserted. So don't use Set in the first place, or, if you really want to keep track of the last element, create a class containing that like this

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

now the question is what is the 'last element inserted'. Imagine your set was empty

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

is now x or y the last inserted? x does not get inserted because y was the last element inserted and x already is an element of the set, on the other hand x from the user's point of view was the last inserted..

For your own, internal purpose, you could "hack" your own Set from any List implementation:

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.
}

That example is slow (O(n) for an add) but, as you are the one implementing it, you can go back to it with smarter code for contains() based on your specifications.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM