尽管已经有几个答案,并且是一个可以接受的答案,但是该主题仍然缺少几点。 首先,共识似乎是使用流解决此问题仅是一种练习,而传统的for循环方法更可取。 其次,到目前为止给出的答案都忽略了使用数组或矢量样式技术的方法,我认为这大大改善了流解决方案。

首先,这是一个常规解决方案,用于讨论和分析:

static List> splitConventional(List input) {

List> result = new ArrayList<>();

int prev = 0;

for (int cur = 0; cur < input.size(); cur++) {

if (input.get(cur) == null) {

result.add(input.subList(prev, cur));

prev = cur + 1;

}

}

result.add(input.subList(prev, input.size()));

return result;

}

这通常很简单,但是有些微妙。 一点是,从prev到prev的待处理子列表始终处于打开状态。 当我们遇到indexes.get(i)时,将其关闭,将其添加到结果列表中,然后前进size。循环之后,我们无条件关闭该子列表。

另一个观察结果是这是一个遍历索引的循环,而不是遍历值本身的循环,因此我们使用算术for循环而不是增强的“ for-each”循环。 但是,这表明我们可以使用索引进行流式处理以生成子范围,而不是通过值进行流式处理并将逻辑放入收集器中(就像乔普·艾肯(Joop Eggen)提出的解决方案一样)。

意识到这一点之后,我们可以看到输入中prev的每个位置都是一个子列表的定界符:它是子列表的右端到左边,而它(加一个)是子列表的左端。 正确的。 如果我们能够处理极端情况,则可以找到一种方法,即找到发生indexes.get(i)元素的索引,将它们映射到子列表,然后收集子列表。

结果代码如下:

static List> splitStream(List input) {

int[] indexes = Stream.of(IntStream.of(-1),

IntStream.range(0, input.size())

.filter(i -> input.get(i) == null),

IntStream.of(input.size()))

.flatMapToInt(s -> s)

.toArray();

return IntStream.range(0, indexes.length-1)

.mapToObj(i -> input.subList(indexes[i]+1, indexes[i+1]))

.collect(toList());

}

获取出现prev的索引非常容易。 绊脚石是在左侧添加indexes.get(i),在右侧添加size。 我选择使用Stream.of进行附加,然后使用flatMapToInt进行拼合。 (我尝试了其他几种方法,但是这似乎是最干净的。)

在这里使用数组作为索引要方便一些。 首先,与数组相比,访问数组的符号更好:prev与indexes.get(i).其次,使用数组避免装箱。

此时,数组中的每个索引值(最后一个除外)都比子列表的起始位置小一个。 其直接右边的索引是子列表的末尾。 我们只需要在数组上流式传输并将每对索引映射到一个子列表中并收集输出。

讨论

流方法比for循环版本略短,但密度更高。 for循环版本很熟悉,因为我们一直在用Java来做这些事情,但是如果您还不知道该循环应该做什么,那么它就不那么明显了。 在弄清楚prev在做什么以及循环结束后必须关闭open子列表的原因之前,您可能必须模拟一些循环执行。 (我最初忘记了它,但是在测试中发现了这一点。)

我认为,流方法更容易概念化正在发生的事情:获取一个指示子列表之间边界的列表(或数组)。 那是一条轻松的两线客流。 正如我上面提到的,困难在于找到一种将边缘值固定到末端的方法。 如果这样做有更好的语法,例如

// Java plus pidgin Scala

int[] indexes =

[-1] ++ IntStream.range(0, input.size())

.filter(i -> input.get(i) == null) ++ [input.size()];

它将使事情变得更加混乱。 (我们真正需要的是数组或列表理解。)一旦有了索引,将它们映射到实际的子列表并将它们收集到结果列表中就很简单了。

当并行运行时,这当然是安全的。

更新2016-02-06

这是创建子列表索引数组的一种更好的方法。 它基于相同的原理,但是它会调整索引范围并为过滤器添加一些条件,以避免必须串联和平整索引。

static List> splitStream(List input) {

int sz = input.size();

int[] indexes =

IntStream.rangeClosed(-1, sz)

.filter(i -> i == -1 || i == sz || input.get(i) == null)

.toArray();

return IntStream.range(0, indexes.length-1)

.mapToObj(i -> input.subList(indexes[i]+1, indexes[i+1]))

.collect(toList());

}

更新2016-11-23

我在Devoxx Antwerp 2016上与Brian Goetz共同发表了一个主题为“并行思考”(视频)的演讲,其中介绍了此问题和我的解决方案。 出现的问题是有一个细微的变化,该变化在“#”上分割,而不是null,但在其他方面相同。 在谈话中,我提到我针对此问题进行了大量的单元测试。 我在下面将它们作为一个独立程序附加到了我的循环和流实现中。 对于读者来说,一个有趣的练习是针对我在此处提供的测试用例运行其他答案中提出的解决方案,并查看哪些失败以及为什么失败。 (其他解决方案将不得不根据谓词进行拆分,而不是对null进行拆分。)

import java.util.*;

import java.util.function.*;

import java.util.stream.*;

import static java.util.Arrays.asList;

public class ListSplitting {

static final Map, List>> TESTCASES = new LinkedHashMap<>();

static {

TESTCASES.put(asList(),

asList(asList()));

TESTCASES.put(asList("a", "b", "c"),

asList(asList("a", "b", "c")));

TESTCASES.put(asList("a", "b", "#", "c", "#", "d", "e"),

asList(asList("a", "b"), asList("c"), asList("d", "e")));

TESTCASES.put(asList("#"),

asList(asList(), asList()));

TESTCASES.put(asList("#", "a", "b"),

asList(asList(), asList("a", "b")));

TESTCASES.put(asList("a", "b", "#"),

asList(asList("a", "b"), asList()));

TESTCASES.put(asList("#"),

asList(asList(), asList()));

TESTCASES.put(asList("a", "#", "b"),

asList(asList("a"), asList("b")));

TESTCASES.put(asList("a", "#", "#", "b"),

asList(asList("a"), asList(), asList("b")));

TESTCASES.put(asList("a", "#", "#", "#", "b"),

asList(asList("a"), asList(), asList(), asList("b")));

}

static final Predicate TESTPRED = "#"::equals;

static void testAll(BiFunction, Predicate, List>> f) {

TESTCASES.forEach((input, expected) -> {

List> actual = f.apply(input, TESTPRED);

System.out.println(input + " => " + expected);

if (!expected.equals(actual)) {

System.out.println(" ERROR: actual was " + actual);

}

});

}

static List> splitStream(List input, Predicate super T> pred) {

int[] edges = IntStream.range(-1, input.size()+1)

.filter(i -> i == -1 || i == input.size() ||

pred.test(input.get(i)))

.toArray();

return IntStream.range(0, edges.length-1)

.mapToObj(k -> input.subList(edges[k]+1, edges[k+1]))

.collect(Collectors.toList());

}

static List> splitLoop(List input, Predicate super T> pred) {

List> result = new ArrayList<>();

int start = 0;

for (int cur = 0; cur < input.size(); cur++) {

if (pred.test(input.get(cur))) {

result.add(input.subList(start, cur));

start = cur + 1;

}

}

result.add(input.subList(start, input.size()));

return result;

}

public static void main(String[] args) {

System.out.println("===== Loop =====");

testAll(ListSplitting::splitLoop);

System.out.println("===== Stream =====");

testAll(ListSplitting::splitStream);

}

}

java实现拆分元素,java-将列表沿元素拆分为子列表相关推荐

  1. java list 截取部分数据_Java List.subList()方法:获取列表中指定范围的子列表

    集合类中的 List.subList() 方法用于获取列表中指定范围的子列表,该列表支持原列表所支持的所有可选操作.返回列表中指定范围的子列表. 语法: subList(int fromIndex,i ...

  2. python子列表_关于python:创建子列表

    本问题已经有最佳答案,请猛点这里访问. 与列表扁平化相反. 给定一个列表和一个长度n返回一个长度n子列表的列表. def sublist(lst, n): sub=[] ; result=[] for ...

  3. Java技术图谱!java高级工程师工资一般多少

    InnoDB总体结构 首先我们来看官网的一张图(图片来源于MySQL官网): 从上图中可以看出其主要分为两部分结构,一部分为内存中的结构(上图左边),一部分为磁盘中的结构(上图右边) 内存结构 Inn ...

  4. 注意!在subList生成子列表之后,一定不要随便更改原列表

    大家好,我是雄雄. 前几期我们说过,subList方法是返回原列表的子列表,并且我们还说过,在subList返回的子列表上操作时,会直接影响着原列表,原文在这里: subList?? subStrin ...

  5. python中的连续比较是什么_Python算法的分治算法,python,之,连续,子,列表,最大,和...

    连续子列表的最大和 在一个列表中找到连续子列表的最大和.列表中的数字可负可正,并且子列表不能为空. 问题提出: 找到以下列表的最大子列表的和: [-2,1,-3,4,-1,2,1,-5,4] 解题思路 ...

  6. 在Java中使用分隔符(拆分的对立面)连接数组元素的快速简便方法

    本文翻译自:A quick and easy way to join array elements with a separator (the opposite of split) in Java [ ...

  7. Java List.size()方法:返回列表中元素的个数(亲测)

    Java 集合类中的 List.size() 方法以 int 形式返回列表中元素的个数. 语法: size() 返回列表中元素的个数.如果列表中元素的个数超过 2 147 483 647,则返回 2 ...

  8. 删除列表包含某个字符串的元素java

    删除列表包含某个字符串的元素 package ellip.dt.missionplanning;import java.util.ArrayList; import java.util.Collect ...

  9. java 对象多属性排序_java list按照元素对象的指定多个字段属性进行排序

    package com.enable.common.utils; import java.lang.reflect.Field; import java.text.NumberFormat; impo ...

最新文章

  1. Kubernetes — 安装 Dashboard UI
  2. 将Html文档整理为规范XML文档
  3. 范式化设计和反范式化设计优缺点
  4. Chrome调试模拟iPhone6时body显示980*1742
  5. leecode62 不同路径
  6. Apt 命令解说(apt-get update、apt-cache search package、apt-get install package、apt-get remove )
  7. Kafka 集群数据备份 MirrorMaker 详解
  8. 验证文件路径的正则表达式(支持网络路径)
  9. Optional的巧用
  10. DevSecOps 现状:云 IT 的复杂度制造了“无法改变的”安全问题
  11. 华为机试HJ64:MP3光标位置
  12. SpringMvc表单使用
  13. [转载]窗口之间的主从关系与Z-Order
  14. python 除法 保留2位小数
  15. JAVA 事务回滚方法调用非事务回滚方法
  16. java 委托 代理 区别_区分委托,组合和聚合(Java OOdevise)
  17. 找直系亲属——并查集
  18. 2021.3.19en
  19. 【编译原理·总复习】第二章||文法语言||语法树||最左最右推导归约||句柄直接短语||例题+知识点
  20. android计算器开源小项目代码(附安装包.apk)

热门文章

  1. Java Scanner的hasNext()方法
  2. 常用的三种线性模型算法--线性回归模型、岭回归模型、套索回归模型
  3. 怎么访问到别人的电脑?
  4. 英国将强制互联网公司遏制外国政府发布假信息,违法者或被罚款数十亿美元...
  5. 酷盟集团旗下酷客SCRM亮相2020 CBME孕婴童展
  6. Allegro导出元器件pad坐标和网络(附带ODB++插件下载)
  7. hadoop集群HA模式(JN+ZK)+yarn搭建
  8. oracle的substr函数的用法
  9. IntelliJ IDEA类和方法注释模板配置
  10. js 生成UUID的几种方法