【问题标题】:How to return N consecutive elements from a Collection?如何从集合中返回 N 个连续元素?
【发布时间】:2011-07-27 19:42:58
【问题描述】:

我传递了一个对象集合(在我的例子中是某个 Contact 类),并且需要从该集合返回一个页面。 我的代码感觉比它需要的要长得多。我是否缺少一些库,这些库可以比像下面那样一次迭代每个元素更优雅地执行?

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

【问题讨论】:

  • 收藏是如何消费的?你用什么方法/打算用什么方法?

标签: java collections iteration paging guava


【解决方案1】:

你可以使用 Guava Iterables.partition:

protected <T> Collection<T> getPageOfContacts(
        Collection<T> contacts, int pageIndex, int pageSize) {
    return Lists.newArrayList(
        Iterables.partition(contacts, pageSize)).get(pageIndex);
}

更复杂的版本不会创建所有页面来选择正确的页面,而是在找到正确的页面时停止。

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
}

更新:

感谢 ColinD 指出:

Iterables.get(Iterables.partition(contacts, pageSize), pageIndex)

是一个更简单的实现。

【讨论】:

  • 如果x 已经是List,那么使用Lists.partition 会好很多。由于参数是 OP 中的 Collection,仍然值得检查集合是否为 List 并尽可能使用 Lists
  • 我会为列表实现一个方法。只有当静态类型信息丢失并且性能很关键时,我才会在上面的方法中添加检查。根据情况,最好在调用者中进行强制转换并直接调用列表版本。
  • 是的,只使用采用List 的重载是有意义的。
  • @ColinD 请参阅下面的评论。 stackoverflow.com/questions/5515955/…
  • 谢谢托马斯。我听说过 Guava lib 在收藏方面创造了奇迹。看起来更好,更干净的代码。向任何人提出后续问题:许多人在这里评论说使用列表会更明智/更好,可能会返回。鉴于我从 TreeSet 获取联系人并且不想假设我的页面的用户会想要 TreeSet 或 List ,这里是否有共识我使用 Collection 作为输入和输出是正确的方法?理想情况下,Java 中会有一个名为“OrderedCollection”的集合接口。
【解决方案2】:

如果您可以要求分页数据为List,则可以使用Guava 轻松获得单个页面的子列表视图:

public <T> List<T> getPage(List<T> list, int pageIndex, int pageSize) {
  return Lists.partition(list, pageSize).get(pageIndex);
}

这不涉及复制或迭代(它使用原始列表的子列表视图)并透明地处理具有少于pageSize 元素的最终页面。

对于任意的IterableCollection,我会这样做:

public <T> List<T> getPage(Iterable<T> iterable, int pageIndex, int pageSize) {
  return Iterables.get(Iterables.partition(iterable, pageSize), pageIndex);
}

通过提供这两种方法,您将能够在编译时高效地处理已知为列表的对象,并尽可能高效地处理任何其他类型的Iterable

【讨论】:

  • 我认为 LinkedLists 这个版本比 Iterable 版本慢。 LinkedLists 的 subList 实现是 O(n)(它使用 get(int),即 O(n))。您应该检查 List 是否实现了 RandomAccess,并为没有实现的 List 添加一个 Iterable 版本。
  • @Thomas:这取决于您将如何使用返回的页面List。如果您要进行基于索引的访问,最好使用Iterable 版本...或者在使用之前将分区复制到RandomAccess 列表中。我认为在很多情况下,页面列表可能只是被迭代,在这种情况下,它应该没问题。
【解决方案3】:

如果您想要元素的定义顺序,您应该使用List,而不是collectionListCollection 之间的基本区别在于List 对元素有固定的顺序。它还定义了非常方便的方法subList(int start, int end),它创建一个子列表,它是原始列表的别名,只包含您想要的元素,而无需将它们复制到新列表的开销。

【讨论】:

  • 然而,意图可能是能够处理任何类型的集合,尤其是列表和集合。在这里,集合似乎是一个合理的候选者,因为联系人很可能是为了消除重复而订购的。
  • @iainmcgin,集合未排序或索引。如果您想翻阅 Set,可以将其复制到 List 中。
  • @Peter TreeSet(更一般地说,SortedSet 实现)基于实现 Comparable 或提供的 Comparator 实例的元素进行排序,并且 LinkedHashSet 具有基于将事物添加到集合中的顺序的顺序。然后,元素的索引基于此顺序是隐式的,因此期望能够对它们进行分页仍然是合理的。这些是我特别考虑的情况,很抱歉没有提前说明这一点。
  • @iainmcgin - 彼得的观点(我认为)和我的观点是不是集合(包括Set 对象)不能被订购。就是“集合”(或“集合”)的抽象概念不包括任何顺序概念。除非您首先对集合进行排序,否则谈论“结果的第 n 页”是没有意义的。当然有CollectionSet 的实现也定义了元素的特定顺序。 (例如,TreeSet 不仅实现了Collection,还实现了SortedSet。)
  • @iainmcgin: List 对于这类事情来说仍然是一个更合适的结构。从特定索引开始的子列表是分页的内容,并且只有列表提供这种类型的访问。您可以分页另一个数据结构,但最好先将要分页的数据放在List 中。
【解决方案4】:

List 接口提供了一个 subList 方法,它接受一个开始索引和一个结束索引。请参阅http://download.oracle.com/javase/6/docs/api/java/util/List.html#subList(int,%20int)。返回的子列表由原始列表支持,因此您可能想要执行类似

的操作
protected Collection<Contact> getPageOfContacts(...) {
    return new ArrayList<Contact>(original.subList(start,end));
}

【讨论】:

    【解决方案5】:
    return new ArrayList<Contact>(new ArrayList<Contact>(contacts).subList(firstElement, lastElement));
    

    注意:这将返回子列表 exclusive lastElement

    注2:由于Kevin提到的原因,结果被复制到另一个列表中。

    【讨论】:

      【解决方案6】:
      Iterables.partition(contacts, pageSize).forEachRemaining(paginatedContacts->{/*Operation here*/});
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-08-19
        相关资源
        最近更新 更多