How to return N consecutive elements from a Collection?
I am passed a collection of objects (some Contact class in my case) and need to return a page from that collection. My code feels much longer than it needs to be. Am I missing some libraries that could perform that more elegantly than iterating over each element one at a time like I do below?
protected Collection<Contact> getPageOfContacts(
Collection<Contact> contacts, int pageIndex, int pageSize) {
if (pageIndex < 0 || pageSize <= 0
|| pageSize > contacts.size()) {
return contacts;
}
int firstElement = pageIndex * pageSize;
int lastElement = (pageIndex + 1) * pageSize - 1;
Collection<Contact> pagedContacts = new ArrayList<Contact>();
int index = -1;
for (Contact contact : contacts) {
index++;
if (index < firstElement) {
continue;
}
if (index > lastElement) {
break;
}
pagedContacts.add(contact);
}
return paged开发者_Python百科Contacts;
}
You could use Guava Iterables.partition:
protected <T> Collection<T> getPageOfContacts(
Collection<T> contacts, int pageIndex, int pageSize) {
return Lists.newArrayList(
Iterables.partition(contacts, pageSize)).get(pageIndex);
}
A more complex version does not create all pages to pick the right one, but stops when the right page is found.
protected <T> Collection<T> getPageOfContacts(
Collection<T> contacts, int pageIndex, int pageSize) {
Iterator<List<T>> partitions = Iterators.partition(contacts.iterator(), pageSize);
for(int page = 0; page<pageSize && partitions.hasNext(); page++){
List<T> partition = partitions.next();
if(page == pageIndex) return partition;
}
return Collections. <T> emptyList(); //or fail
}
Update:
Thanks to ColinD to point out that:
Iterables.get(Iterables.partition(contacts, pageSize), pageIndex)
is a simpler implementation.
If you can require the data to be paged to be a List
, you can get a sublist view of a single page easily using Guava:
public <T> List<T> getPage(List<T> list, int pageIndex, int pageSize) {
return Lists.partition(list, pageSize).get(pageIndex);
}
This involves no copying or iteration (it uses sublist views of the original list) and handles a final page that has fewer than pageSize
elements transparently.
For an arbitrary Iterable
or Collection
, I'd do this:
public <T> List<T> getPage(Iterable<T> iterable, int pageIndex, int pageSize) {
return Iterables.get(Iterables.partition(iterable, pageSize), pageIndex);
}
By providing both these methods, you'd be able to handle objects that are known to be lists at compile-time efficiently and any other type of Iterable
as efficiently as you can.
If you want a defined order to your elements, you should be using a List
, not a collection
. The basic difference between List
and Collection
is that List
has a fixed order to the elements. It also defines the very convenient method subList(int start, int end)
which creates a sub-list which is an alias of the original list containing just the elements you want without the overhead of copying them to a new list.
The List interface provides a subList method, that takes a start index and an end index. See http://download.oracle.com/javase/6/docs/api/java/util/List.html#subList(int,%20int). The returned sublist is backed by the original list, so you probably want to do something like
protected Collection<Contact> getPageOfContacts(...) {
return new ArrayList<Contact>(original.subList(start,end));
}
return new ArrayList<Contact>(new ArrayList<Contact>(contacts).subList(firstElement, lastElement));
Note: this will return the sublist exclusive lastElement
Note 2: The result are copied to another list for the reasons mentioned by Kevin.
Iterables.partition(contacts, pageSize).forEachRemaining(paginatedContacts->{/*Operation here*/});
精彩评论