Lambda收集在使用者方法中创建的元素

Joh*_*ler 9 java lambda java-8 java-stream

我正在从丑陋的嵌套for循环转换为java中漂亮的设计lambda表达式.

这是我的实际代码

for (String foo : foos) {
    for (Bar bar : bars) {    
        if (bar.getFoo().equals(foo)) {
            FooBar fooBar = new FooBar();                           
            fooBar.setBar(bar);
            listOfFooBar.add(fooBar);
            break;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我的实际lambda代码替换上面的代码

foos.forEach(i -> bars.stream().filter(p -> p.getFoo().equals(i)).findFirst().ifPresent(p -> {
        FooBar s = new FooBar();
        fooBar.setBar(bar);
        listOfFooBar.add(fooBar);
    }));
Run Code Online (Sandbox Code Playgroud)

我的问题是,有一种方法可以填充listOfFooBar某种collect()方法吗?

就像是 listOfFooBar = foos.forEach(.....).collect(Collectors.toList());

一个事实是,条形图总是包含每个foo,foos基本上是条形图的一小部分.

如果有更好的方法(在性能或优雅方面)做那个lambda,请分享.

a b*_*ver 5

如果你想要整整九码:

List<FooBar> listOfFooBar = foos.stream()
  .flatMap(foo -> bars.stream().filter(bar-> bar.getFoo().equals(foo)).findFirst()
                    .map(Stream::of).orElse(Stream.empty()))
  .map(bar -> {
                FooBar fooBar = new FooBar();
                fooBar.setBar(bar);
                return fooBar;
              })
  .collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

如果你有一个FooBar接受a 的构造函数,Bar那么你可以保存一些行并写入

.map(FooBar::new)
Run Code Online (Sandbox Code Playgroud)

Java 9中的FWIW您将能够编写

.findFirst().stream()
Run Code Online (Sandbox Code Playgroud)

假设一个合适的构造函数,它将缩短为

List<FooBar> listOfFooBar = foos.stream()
  .flatMap(foo -> bars.stream().filter(bar-> bar.getFoo().equals(foo)).findFirst().stream()))
  .map(FooBar::new)
  .collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

编辑:使用@Misha的建议你可以进一步缩短它:

List<FooBar> listOfFooBar = foos.stream()
  .flatMap(foo -> bars.stream().filter(bar-> bar.getFoo().equals(foo)).limit(1)))
  .map(FooBar::new)
  .collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

  • 你可以用`.limit(1)替换`.findFirst().map(Stream :: of).orElse(Stream.empty())` (3认同)

ass*_*ias 5

由于每个Foo只有一个Bar,你可以从创建一个将Foos连接到Bars的地图开始:

Map<String, Bar> barsByFoo = bars.stream().collect(toMap(Bar::getFoo, b -> b));
Run Code Online (Sandbox Code Playgroud)

如果你有更多的酒吧而不是foos,你可以过滤:

Map<String, Bar> barsByFoo = bars.stream()
                                 .filter(b -> foos.contains(b.getFoo()))
                                 .collect(toMap(Bar::getFoo, b -> b));
Run Code Online (Sandbox Code Playgroud)

然后可以编写嵌套的for循环:

List<FooBar> listOfFooBar = foos.stream()
        .map(barsByFoo::get)
        .filter(Objects::nonNull)
        .map(FooBar::new)
        .collect(toList());
Run Code Online (Sandbox Code Playgroud)

这假设有一个FooBar(Bar)构造函数.

或者您可以从另一方面解决问题并使用(我认为)等效的算法(Set<Foo>在这种情况下您可能会受益于使用a ):

List<FooBar> listOfFooBar = bars.stream()
        .filter(bar -> foos.contains(bar.getFoo()))
        .map(FooBar::new)
        .collect(toList());
Run Code Online (Sandbox Code Playgroud)

无论哪种方式,它通常有助于退出您的初始循环,因为不同的算法/方法通常有利于干净的lambda解决方案.

  • @JohnnyWiller如果`foos`是一组foos它不会比你的嵌套循环慢 - 实际上可能更快.如果它是foos列表,那么只需使用`Set <String> fooSet = new HashSet <>(foos);`然后使用`fooSet :: contains`.除非条形列表比foos列表大得多. (3认同)