Java流的优雅解决方案,避免多次循环。

huangapple go评论75阅读模式
英文:

Java stream elegant solution to avoid looping multiple times

问题

class Note{
    private text;
    ..
    private int score = 0;
}

class Project{
    ...
    List<Note> notes;
    private int score = 0;
}

项目得分由项目属性和所有笔记分数之和计算得出

首先我正在更新和替换项目中的所有笔记然后再次迭代以计算笔记分数

project.notes(project.notes()
        .stream()
        .map(this::updateNote)
        .collect(Collectors.toList()));

project.score(project.notes()
                .stream()
                .mapToInt(n->n.score())
                .sum());

private Note updateNote(Note note){
    note.score(....);
    return note;
}

不过我觉得这样做不够优雅有没有一种更好的解决方案可以避免两次循环
英文:
class Note{
    private text;
    ..
    private int score = 0;
}

class Project{
    ...
    List&lt;Note&gt; notes;
    private int score = 0;
}

Project score is derived by project properties + sum of note scores.

First I'm updating and replacing all notes in the project. Then iterating again to sum the note score.

 project.notes(project.notes()
        .stream()
        .map(this::updateNote)
        .collect(Collectors.toList()));

 project.score(project.notes()
                .stream()
                .mapToInt(n-&gt;n.score())
                .sum());

private Note updateNote(Note note){
    note.score(....);
    return note;
}

Somehow I feel this is not right. Is there an elegant solution to avoid looping twice?

答案1

得分: 1

在类似于映射操作中执行具有副作用的方法,无论如何切分这个谜题,都是可疑的,它总是会显得有些奇怪。

你在这里滥用了映射:你将对象映射到其自身,但在这样做的同时,引发了副作用。嗯,不管怎样,我想 - 这就是可疑的地方,但代码是可行的(只是不好的风格)。还要注意,将收集到的列表传递给 project.notes 没有任何作用,只需 project.notes().stream().map(this::updateNote).collect(Collectors.toList()),让集合已经丢失到了其他地方。收集的唯一目的只是强制流实际迭代(映射不会导致迭代,它只是说:当您开始迭代时,逐步进行映射)。

所以:

project.notes()
  .stream()
  .map(this::updateNote) // 流中的无操作。只是让副作用发生
  .mapToInt(this::score)
  .sum();

这就是你所需要的一切 - 但还是有点可疑。如果 updateNote 而不是更改了 Note 对象,并且有一个 calculateScore 方法,你可以这样做:

project.notes()
    .stream()
    .mapToInt(this::calculateScore)
    .sum();

在这里,calculateScore 不会改变 Note 对象的任何内容,它只是计算分数并返回,而不改变任何字段。

编辑:我忘记了一个流中的 'stream',并添加了一个澄清。

英文:

Performing a side-effect-ful method in a map operation like this is suspect no matter how you want to slice this puzzle, it's always going to look a bit weird.

You're abusing map here: You map objects to itself, but in so doing, cause a side effect. well, in for a penny, in for a pound, I guess - that is the suspect thing here, but the code works (it's just bad style). Note also that passing the collected list to project.notes does nothing, just project.notes().stream().map(this::updateNote).collect(Collectors.toList()) and letting the collection be lost to the either already 'works'. The only point of collecting is merely to force the stream to actually iterate (map doesn't cause iteration, it merely says: When you start iterating, map as-you-go).

So:

project.notes()
  .stream()
  .map(this::updateNote)) // no-op streamwise. Just making the side effect happen
  .mapToInt(this::score)
  .sum();

is all you need - but it's.. still a bit stinky. If instead of updateNote, note was immutable and there is a calculateScore method, you could do:

project.notes()
    .stream()
    .mapToInt(this::calculateScore)
    .sum()

Here, calculateScore doesn't change anything about a Note object, it merely.. calculates the score and returns it, without changing any fields.

EDIT: I forgot a 'stream' in stream, and added a clarification.

答案2

得分: 1

你可以通过在AtomicInteger中累积总和来避免两次循环,但这将导致将方法引用this::updateNote替换为lambda表达式:

AtomicInteger sum = new AtomicInteger(0);

project.notes(project.notes()
        .stream()
        .map(note -> {
            Note updated = updateNote(note);
            sum.addAndGet(updated.getScore());
            return updated;
        })
        .collect(Collectors.toList()));

project.score(sum.intValue());
英文:

You may get rid of looping twice by accumulating the sum in an AtomicInteger, but this would result in replacing the method reference this::updateNote with a lambda:

AtomicInteger sum = new AtomicInteger(0);

project.notes(project.notes()
        .stream()
        .map(note -&gt; {
            Note updated = updateNote(note);
            sum.addAndGet(updated.getScore());
            return updated;
        })
        .collect(Collectors.toList()));

project.score(sum.intValue());

huangapple
  • 本文由 发表于 2020年10月19日 00:19:11
  • 转载请务必保留本文链接:https://go.coder-hub.com/64415569.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定