【问题标题】:Java 8 Streams find element and add it to the start of the new ListJava 8 Streams 查找元素并将其添加到新列表的开头
【发布时间】:2018-07-25 12:57:54
【问题描述】:

我想知道这是否可以使用 Java Streams 用一行来解决这个问题:我需要在字符串列表中找到一个字符串并创建另一个列表,其中搜索字符串(如果找到)将是第一个字符串新的列表,然后是其余的字符串值

例如:

List<String> oldList = Arrays.asList("Avacado", "Apple", "Orange", "Chocolate");

所以现在如果使用过滤器“Chocolate”进行搜索,它应该返回一个新列表,其中包含“Chocolate”、“Avacado”、 “苹果”、“橙子”。

List<String> newList =  oldList.stream().filter(i -> i.equals("Chocolate")).collect(Collectors.toList()) ???

【问题讨论】:

  • 看起来你想对元素进行排序。

标签: java list java-8 java-stream


【解决方案1】:

您正在使用Stream 寻求解决方案。这是一个:

    List<String> sorted = oldList.stream()
            .sorted(Comparator.comparing("Chocolate"::equals).reversed())
            .collect(Collectors.toList());
    System.out.println(sorted);

它给出了:

[Chocolate, Avacado, Apple, Orange]

所以只有"Chocolate" 在第一个索引处被移动。

但实际上你不需要Stream。列表本身就足够了:

oldList.sort(Comparator.comparing("Chocolate"::equals).reversed());
System.out.println(oldList);

结果是

[Chocolate, Avacado, Apple, Orange]

...同样的。

编辑:
我已经更新了我的答案,因为@Holger 指出它违反了Comparator.compare(o1,o2) 的合同。我正在使用 Holger 在他的评论中建议的解决方案。所以他应该得到支持,因为我挣扎了两次才找到正确的解决方案。

这种方法将每个字符串映射到一个boolean 值,该值表明该字符串是否等于"Chocolate"。在相等的情况下,Boolean.compare(x,y) 将返回 1 for x-1 for y 如果另一个字符串不等于 "Chocolate"。如果xy 等于"Chocolate",则结果为0
当我们想将"Chocolate" 移动到第一个索引时,它的索引必须减少。因此需要一个反向比较器(1 => -1-1 => 1)。

【讨论】:

  • @Joe 不客气。实际上它是排序......但只影响一个特定元素:)
  • 这恰好是“意外工作”。您省略的部分是如果第一个参数大于第二个参数,比较器应该返回一个正数。所以你的比较器,(l, r) -&gt; "Chocolate".equals(l) ? -1 : 0 是不一致的并且违反了合同。但是实现正确的Comparator 比您想象的要容易。只需使用Comparator.comparing("Chocolate"::equals).reversed()...
  • 对了,你为什么用System.out.println(Arrays.toString(sorted.toArray()));而不是简单的System.out.println(sorted);
  • 这很棘手。您的比较器仍然不正确。只需将其存储在变量中并运行String[] x = { "Chocolate", "Apple" }; for(String a: x) for(String b: x) System.out.println(a+" () "+b+": "+(comparator.compare(a, b))); 即可测试所有可能的组合。正确的变体是(l, r) -&gt; ("Chocolate".equals(l)? 0: 1) - ("Chocolate".equals(r)? 0: 1)。我想,你现在明白为什么我更喜欢工厂方法Comparator.comparing...
  • 没关系。最后,这是一个很棒的练习,展示了手动比较器实现的复杂性以及这些新工厂方法的实用性。
【解决方案2】:

您的要求基本上是关于排序列表,因此您可以使用sort 方法:

List<String> newList =  
    oldList.stream()
           .sorted((i1,i2) -> i1.equals("Chocolate")?
                           -1: // untested, so may be the other way around...
                           i2.equals("Chocolate")?
                             1:
                             i1.compareTo(i2))
           .collect(Collectors.toList())

[edit:] 感谢@Holger,我们可以简化为:

List<String> newList =  
    oldList.stream()
           .sorted(Comparator.comparing("Chocolate"::equals).reversed())
           .collect(Collectors.toList())

【讨论】:

  • 我喜欢这种方法
  • 这个比较器也会对所有其他元素进行排序,这不是原始发布者想要的。
  • 好的,但是您可以将i1.compareTo(i2) 替换为0 来避免这种情况。
  • 本次复杂度o(nlog(n))
  • Boolean本身就是Comparable似乎被广泛忽视了。所以你可以简单地使用Comparator.comparing("Chocolate"::equals).reversed()"Chocolate" 移动到前面。 @sajib O(n log n) 是排序算法的最坏情况复杂度。目前使用的 TimSort 算法非常擅长利用预排序元素的跨度,并且在根据布尔属性进行排序时,几乎所有内容都是预排序的,因此该场景在实践中接近 O(n)
【解决方案3】:

您可以在收集没有targetElement的列表后添加新列表的第一个位置。

List<String> newList =  oldList.stream().filter(i -> !i.equals("Chocolate")).collect(Collectors.toList());
    if(oldList.size() != newList.size()){
        newList.add(0,"Chocolate");
    }

【讨论】:

  • 感谢 Sajib,是的,这是正确的,但我想知道我们是否可以使用流函数在一行中执行此操作,而不是在单独的语句中将搜索到的字符串添加到第 0 位。
  • 这种方法总是将“巧克力”项添加到结果中,即使它没有在输入中。
  • 可以用size查看
  • 那么它不再是“oneliner”了,但是,OP显然不在乎,我只是想提一下。
  • 如果你想进入那个方向(不使用排序),你最好使用List&lt;String&gt; newList = new ArrayList&lt;&gt;(oldList); int ix = newList.indexOf("Chocolate"); if(ix &gt; 0) Collections.rotate(newList.subList(0, ix+1), 1);,它避免在列表的开头插入,从而使用尽可能少的数据移动。但是你必须考虑是否应该处理多次出现。
【解决方案4】:

我个人认为 Streams 不适合这项任务。在我看来,执行此操作的程序方式更具可读性。

这是我的解决方案。注释代码是这样做的程序方式。为方便起见,我使用整数,但字符串是相同的。

        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
//        int index = list.indexOf(3);
//        ArrayList<Integer> resultList = new ArrayList<>();
//        resultList.add(list.get(index));
//        resultList.addAll(list.subList(0, index));
//        resultList.addAll(list.subList(index + 1, list.size()));
        List<Integer> resultList = Stream.concat(Stream.of(3), list.stream().filter(x -> x == 3)).collect(Collectors.toList());
        System.out.println(resultList);

【讨论】:

  • 但是如果3 不在流中怎么办?
  • @TimothyTruckle 接受的答案也不处理这个问题并且被接受。所以我假设要搜索的项目总是存在并且只出现一次。
  • @TimothyTruckle 另外,从 OP 的语气来看,我认为这是一种“有趣”的挑战来做单线。我可能错了。
  • 我不知道接受的答案是否发生了变化,但the currently accepted answer 确实正确处理了没有匹配元素以及多次出现的情况。
  • @Holger 接受的答案确实发生了变化。我指的是sajib的回答。
猜你喜欢
  • 2019-12-06
  • 1970-01-01
  • 2011-07-17
  • 1970-01-01
  • 2021-04-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-06-02
相关资源
最近更新 更多