Java 8,基于另一个列表索引的流

Posted

技术标签:

【中文标题】Java 8,基于另一个列表索引的流【英文标题】:Java 8, stream with another List Index based on 【发布时间】:2019-09-24 15:22:37 【问题描述】:

我有这个代码

List<Integer> numbers = new ArrayList<>(30, 25, 17, 12 ,8, 5, 3, 2));
List<Integer> indices = new ArrayList<>(5, 3, 2));
Integer newNumber = 1;
for (int i = indices.size() - 1; i >= 0; i--) 
  newNumber *= (numbers.get(indices.get(i)));

newNumber 将是:5*12*17 = 1020

可以使用stream reduce吗?

稍后我需要从原始数字中删除索引(我在过滤器中考虑,但目标是索引而不是 Integer 对象)。

List<Integer> newNumbers = new ArrayList<>(numbers);
for (int i = indices.size() - 1; i >= 0; i--) 
  newNumbers.remove(indices.get(i).intValue()); // Remove position

另外,我在考虑这段代码。

List<Integer> newNumbers2 = new ArrayList<>();
for (int i = 0; i < numbers.size(); i++) 
  if (!indices.contains(i)) 
    newNumbers2.add(numbers.get(i));
  

是否可以使用流来做到这一点?

谢谢。

【问题讨论】:

你为什么需要/想要这里的流媒体? 我执行复杂操作,流允许我并行。 【参考方案1】:

是的,您可以使用简单的归约来做到这一点。在这种情况下,归约的单位元是 1。

int product = indices.stream().mapToInt(numbers::get).reduce(1, (n1, n2) -> n1 * n2);

后一个问题的答案是,

Set<Integer> indicesSet = new HashSet<>(indices);
List<Integer> newNumbers = IntStream.range(0, numbers.size())
    .filter(n -> !indicesSet.contains(n))
    .mapToObj(n -> numbers.get(n))
    .collect(Collectors.toList());

在数学中,单位元素是一个特殊类型的元素 集相对于该集上的二元运算,它留下任何 与它组合时集合的元素不变。

【讨论】:

【参考方案2】:

假设 索引按相反顺序排序,使用顺序流的两个操作的单个管道可能是:

int product = indices.stream().sequential()
        .mapToInt(index -> numbers.remove((int) index))
        .reduce(1, (a, b) -> a * b);

注意:这里使用List.remove 是为了从索引中删除一个元素而不是从对象中删除,因此转换为(int)

【讨论】:

但是即使索引按照您的需要排序,Stream API 也不保证 处理顺序 会匹配。它恰好适用于顺序流,但您不应该依赖它。 @Holger 感谢您的参与。我明白处理顺序保证是解决方案的风险,我确实引用了它,牢记顺序流。好奇:为什么不应该依赖顺序流来使该解决方案起作用?请告诉我,如果细节值得分享,我会更乐意将其作为一个单独的问题提出。 处理顺序与顺序执行中遇到的顺序一致是一个实现细节,但不是保证行为。仅仅因为我们今天无法想象,一个实现如何利用权限以不同的顺序处理元素,我们不能假设情况总是如此。另一点是以无意的方式使用 API 是危险的,因为下一位读者可能没有意识到脆弱性,并且以一种看起来有效但破坏它的方式更改代码或将其部分复制到另一个上下文,但它并不总是有效。【参考方案3】:

使用列表索引从列表编号中删除索引。

List<Integer> newNumbers = numbers.stream().filter(num ->           
            !indices.contains(numbers.indexOf(num)) 
    ).collect(Collectors.toList());

【讨论】:

【参考方案4】:

为了防止 int 溢出,我建议使用 Math.multiplyExact() 进行乘法运算。为了防止IndexOutOfBoundsExceptions,您应该首先过滤索引:

int newNumber = indices.stream()
        .filter(i -> i < numbers.size())
        .mapToInt(numbers::get)
        .reduce(Math::multiplyExact)
        .orElseThrow();

如果您想将数字相乘,您应该将它们映射为长值:

long nNumber = indices.stream()
        .filter(i -> i < numbers.size())
        .mapToLong(numbers::get)
        .reduce(Math::multiplyExact)
        .orElseThrow();

如果您想为空流返回默认值,则可以使用orElse() 替代orElseThrow()

要从 numbers 数组中删除索引,您可以使用以下命令:

indices.stream()
        .filter(i -> i < numbers.size())
        .sorted(Comparator.reverseOrder())
        .mapToInt(i -> i)
        .forEach(numbers::remove);

【讨论】:

以上是关于Java 8,基于另一个列表索引的流的主要内容,如果未能解决你的问题,请参考以下文章

Jolt-基于索引需要迭代一个列表并从索引中形成公共对象

用 Jquery 中另一个 html 列表的索引填充一个 html 列表

Pandas - 根据另一个列表中的索引对列表中的值求和

如何使用流将列表转换为带有索引的地图 - Java 8?

基于Java中另一个arraylist中的对象值对arraylist进行排序

使用 Perl 中对列表进行排序的索引对另一个列表进行排序和索引