编辑 - 2017年11月28日
正如用户@Emiel在注释中建议的那样,执行此操作的最佳方法是用于通过一系列索引来驱动列表:Stream.itearate
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
int skip = 3;
int size = list.size();
// Limit to carefully avoid IndexOutOfBoundsException
int limit = size / skip + Math.min(size % skip, 1);
List<Integer> result = Stream.iterate(0, i -> i + skip)
.limit(limit)
.map(list::get)
.collect(Collectors.toList());
System.out.println(result); // [1, 4, 7, 10]
这种方法没有我之前的答案的缺点,下面是(出于历史原因,我决定保留它)。
另一种方法是使用以下方式:Stream.iterate()
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
int skip = 3;
int size = list.size();
// Limit to carefully avoid IndexOutOfBoundsException
int limit = size / skip + Math.min(size % skip, 1);
List<Integer> result = Stream.iterate(list, l -> l.subList(skip, l.size()))
.limit(limit)
.map(l -> l.get(0))
.collect(Collectors.toList());
System.out.println(result); // [1, 4, 7, 10]
这个想法是创建一个子列表流,每个子列表跳过前一个子列表的第一个元素(在示例中)。N
N=3
我们必须限制迭代次数,这样我们就不会尝试获取边界超出范围的子列表。
然后,我们将子列表映射到它们的第一个元素并收集结果。根据源列表,保留每个子列表的第一个元素按预期工作,因为每个子列表的起始索引都会将元素向右移动。N
这也是有效的,因为该方法返回原始列表的视图,这意味着它不会为每次迭代创建一个新的列表。List.sublist()
List
编辑:过了一会儿,我了解到采用@sprinter的方法之一要好得多,因为围绕原始列表创建了一个包装器。这意味着流的第二个列表将是第一个列表的包装器,流的第三个列表将是第二个列表的包装器(已经是包装器!),依此类推...subList()
虽然这可能适用于中小型列表,但应该注意的是,对于非常大的源列表,将创建许多包装器。这可能最终变得昂贵,甚至生成.StackOverflowError