5

Is there a one-liner (maybe from Guava or Apache Collections) that gets a sublist from a set. Internally it should do something like this:

public <T> List<T> sublist(Set<T> set, int count) {
  Iterator<T> iterator = set.iterator();
  List<T> sublist = new LinkedList<T>();
  int pos = 0;
  while (iterator.hasNext() && pos++ < count) {
    sublist.add(iterator.next());
  }
  return sublist;
}

Obviously, if there are not enough elements it has to return as many as possible.

Dmytro
  • 16,668
  • 27
  • 80
  • 130
yegor256
  • 102,010
  • 123
  • 446
  • 597
  • 1
    If you want `subList` from your collection, you shouldn't use `Set` in first place since its iteration order is **undefined** and your implementation can yield different results depending on... phase of the moon. – Grzegorz Rożniecki Sep 19 '12 at 09:29
  • How do you know that order of elements in my `Set` is undefined? – yegor256 Sep 19 '12 at 11:40
  • From Java Collections API - see [iterator() description in `Set`](http://docs.oracle.com/javase/6/docs/api/java/util/Set.html#iterator()). The `Set` interface itself makes adds nothing to `Collection` contract about iteration order; Set implementations on the other hand [_can_ order its elements somehow](http://docs.oracle.com/javase/tutorial/collections/interfaces/set.html) (TreeSet and LinkedHashSet does). – Grzegorz Rożniecki Sep 19 '12 at 11:48
  • How do you know that my `Set` implementation is not adding iteration order to it? – yegor256 Sep 19 '12 at 16:21
  • 1
    I don't know your impl, but in method signature there's no `YourSet` but `Set` parameter and thus method can (you don't forbid it) be invoked as `sublist(new HashSet(Arrays.asList(2,3,1), 2)` which _can_ yeild different result than `sublist(new LinkedHashSet(Arrays.asList(2,3,1), 2)`. – Grzegorz Rożniecki Sep 19 '12 at 16:39
  • `Set` iteration order is not undefined, as you said above. It may be defined by `Set` interface in any possible way. Keep in mind that `HashSet` and `LinkedHashSet` are just a few possible implementations. – yegor256 Sep 19 '12 at 17:10

7 Answers7

18

With Guava:

return FluentIterable.from(set) 
  .limit(count)
  .toImmutableList();

(Also, this won't actually iterate over the whole set, in contrast to most of these other solutions -- it'll actually only iterate through the first count elements and then stop.)

Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
13
(new LinkedList<Object>(mySet)).sublist(0, Math.min(count, mySet.size()))

But please note: the code (even your original code) is a little bit smelly, since iteration order of sets depends on the actual set implementation in question (it's totally undefined in HashSet and the key order for TreeSets). So, it is actually an open question, which elements make it into the final sublist.

Dirk
  • 30,623
  • 8
  • 82
  • 102
7

This should do it:

return (new LinkedList<T>(set)).subList(0, count);

But ensure, that count isn't larger than the size of set.

Baz
  • 36,440
  • 11
  • 68
  • 94
5

You could use a TreeSet and use it's subSet method:

Returns a view of the portion of this set whose elements range from fromElement to toElement. If fromElement and toElement are equal, the returned set is empty unless fromExclusive and toExclusive are both true. The returned set is backed by this set, so changes in the returned set are reflected in this set, and vice-versa. The returned set supports all optional set operations that this set supports.

EXAMPLE USING INTEGER:

TreeSet<Integer> t = new TreeSet<Integer>();
t.add(1);
t.add(2);
t.add(3);
t.add(4);
t.add(5);

System.out.println("Before SubSet:");

for(Integer s : t){
    System.out.println(s);
}

System.out.println("\nAfter SubSet:");


for(Integer s : t.subSet(2,false,5,true)){
    System.out.println(s);
}

OUTPUT:

Before SubSet:
1
2
3
4
5

After SubSet:
3
4
5

Alternatively, If you do not know the elements and want to return the elements between two points you can use an ArrayList constructed with the Set and use the subList method.

System.out.println("\nAfter SubSet:");

t = new TreeSet(new ArrayList(t).subList(2, 5));

for(Integer s : t){
    System.out.println(s);
}
Chris B
  • 925
  • 4
  • 14
  • that's against the requirements explained above. I don't know the values of elements, I just need to get a number of them – yegor256 Sep 19 '12 at 09:20
  • @yegor256 I do apoligise. I have added a method onto the end which I think may be more along the lines of what you wanted? – Chris B Sep 19 '12 at 09:45
3

What about this

Set<String> s = new HashSet<String>();
// add at least two items to the set 
Set<String> subSet = new HashSet(new ArrayList<String>(s).subList(1, 2));

This would sublist between 1 and 2

RNJ
  • 15,272
  • 18
  • 86
  • 131
0

Without creating a copy of the Set beforehand, you can do (using Guava) :

Lists.newLinkedList(Iterables.getFirst(Iterables.partition(mySet, count), ImmutableList.of()))

It's a real LinkedList containing only (up to) the first count elements, not a view on a larger list.

Frank Pavageau
  • 11,477
  • 1
  • 43
  • 53
0

with java 8

set.stream().limit(limit).collect(Collectors.toList());
Adil Karaöz
  • 216
  • 1
  • 11