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

时间:2015-09-17 13:55:36

标签: 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;
        }
    }
}

我的实际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);
    }));

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

listOfFooBar = foos.forEach(.....).collect(Collectors.toList());

这样的东西

一个事实是,酒吧总是包含每一个foo,foos基本上只是酒吧的一小部分。

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

3 个答案:

答案 0 :(得分:5)

如果你想要整整九码:

FooBar

如果您的Bar构造函数接受.map(FooBar::new) ,那么您可以保存一些行并写入

.findFirst().stream()

Java 9中的FWIW你可以写

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

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

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

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

    <script type='text/javascript' src='https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js'></script>
<SCRIPT type='text/javascript' SRC='https://swx.cdn.skype.com/shared/v/1.1.23.0/SkypeBootstrap.min.js'></SCRIPT>
    <TABLE CLASS='test'>
<TR><TD ALIGN=RIGHT><INPUT TYPE=TEXT class=suptabde NAME="username" ID="username" VALUE="" MAXLENGTH=40 style="width:18em">
<INPUT TYPE=PASSWORD class=testtabde NAME="password" VALUE="" MAXLENGTH=40 style="width:18em">
<BUTTON id='signin' class='testtabde' STYLE='text-align:center' VALUE='  Skype Login  '>Log in</BUTTON><BUTTON id='signout' class='suptabde' STYLE='text-align:center' VALUE='  Skype Login  '>Log out</BUTTON>
</TABLE>

答案 1 :(得分:5)

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

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

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

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

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

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

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

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

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

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

答案 2 :(得分:0)

如果@SuppressLint("WrongCall") @Override public void run() { Canvas canvas; while (m_Running) { canvas =null; try { canvas =this.m_SurfaceHolder.lockCanvas(); Rect Source =new Rect(0,0,1024,768); Rect Dest =new Rect(0,0,m_View.getWidth(),m_View.getHeight()); canvas.drawBitmap(m_SimpleBitmap, 0, 0, null); canvas.drawBitmap(m_BackBuffer,Source,Dest,null); } finally { if (canvas!=null) m_SurfaceHolder.unlockCanvasAndPost(canvas); } } } } 的构造函数接受FooBar作为参数:

Bar

然后,你可以这样做:

public class FooBar {

    public FooBar(Bar bar) {
        // do something with bar, assign it, etc
    }
}

这会流式传输您的List<FooBar> fooBars = foos.stream() .map(foo -> bars.stream() .filter(bar -> bar.getFoo().equals(foo)) .findFirst() .map(FooBar::new)) .filter(Optional::isPresent) .map(Optional::get) .collect(Collectors.toList()); ,并为每个foos流式传输您的foo,直到找到与当前bars匹配的第一个foo。如果实际找到了foo,则会从内部流的当前FooBar创建新的bar。这为我们留下了Optional<FooBar>的流,然后将其过滤以仅保留非空的选项。然后,将选项转换为它们包含的值(这是在上一步中创建的FooBar),最后,这些FooBar被收集到List<FooBar>

编辑:这是我的第一次尝试。使用@ zeroflagL的方法要好得多:

List<FooBar> fooBars = foos.stream()
    .flatMap(foo -> bars.stream()
        .filter(bar -> bar.getFoo().equals(foo))
        .findFirst()
        .map(Stream::of).orElse(Stream.empty()))
    .map(FooBar::new)
    .collect(Collectors.toList());