【问题标题】:How to iterate a List and set data to it in most efficient way? [closed]如何以最有效的方式迭代列表并为其设置数据? [关闭]
【发布时间】:2021-06-13 06:44:40
【问题描述】:

我正在处理一个问题,我需要迭代一个 N 大小的数组列表并与另一个列表进行比较,对于所有匹配的元素,我需要在其上设置一些数据。下面给出了示例和我到目前为止所尝试的内容。

List<Emp> emp= new ArrayList<>(); 
List<Student> stud = new ArrayList<>();
 
class Emp -> int id, String status, String name; 
class Student -> int id, String studStatus, String name;

如果学生和员工的 id 匹配,则使用一些默认值更新 emp 状态。

到目前为止,我尝试了此代码,但它并没有帮助我获得所需的结果。纠正我在这里做错了什么?

  stud.stream().filter(i -> emp.stream().filter(j ->
 j.getId().equals(i.getId()))
                                .map(i -> i.setStatus(Status.PASS)));

【问题讨论】:

  • 请提供实际代码而不是伪代码,并阅读stackoverflow.com/editing-help以帮助格式化。
  • 使用列表迭代器甚至只是一个 for 循环都可以。
  • 我刚刚格式化了代码..抱歉!

标签: java data-structures java-8 java-stream


【解决方案1】:

map 是一个中间操作。除非您向其附加终端操作,否则不会调用流管道。

为避免二次时间复杂度,通过构造 Map&lt;Integer, Emp&gt; 进行预处理。

Map<Integer, Emp> empData = emp.stream()
     .collect(Collectors.toMap(Emp::getId, Function.identity()));

如果有多个Emp 对象具有相同的Id,这将引发异常。

接下来,遍历学生,从上面的地图中获取到合适的Emp实例并改变状态

stud.stream()
    .filter(student -> empData.containsKey(student.getId()))
    .forEach(student -> empData.get(student.getId()).setStatus(Status.PASS));

【讨论】:

    【解决方案2】:

    最简单的方法是首先创建一组所有学生 ID,如下所示:

    Set<Integer> studentIds = stud.stream()
         .map(Student::getId)
         .collect(Collectors.toSet());
    

    通过使用Set 而不是List,我们还可以避免重复ID。

    现在我们可以遍历员工并查看员工 ID 是否包含在学生 ID 集中。如果是这样,我们会更新员工的状态。

    emp.stream()
        .filter(studentIds::contains)
        .forEach(employee -> employee.setStatus(Status.PASS));
    

    【讨论】:

    • 您应该收集员工的 ID,然后流过学生以保持原始代码的逻辑,否则,这是一个非常好的方法。
    【解决方案3】:

    您的代码中有两点需要注意:

    1. .filter 应该返回一个布尔值,以便过滤记录。但在你的情况下,这并没有发生。您应该对内部过滤器输出进行计数,看看它是否大于 0。或者,简化此操作并使用 anyMatch
    2. .map 需要相同实体/类的响应。但是当你使用 setter 时,你会返回 void,因此你的 setter 并没有真正生效。要么使用不同的自定义设置器返回更新的对象,要么使用代码块返回更新的对象。
    3. .map 不是终端操作。因此,整个表达式甚至都没有被执行。你会想要一个像收集这样的终端操作,甚至只是一个计数来让轮子运转
    4. 看起来您正在为 Status 使用枚举,但这似乎不是定义类的方式...

    如果您想保留您的代码并进行更正.. 以下是您可以执行的操作

        stud.stream()
            .filter(i -> emp.stream().filter(j -> j.getId() == i.getId()).count() > 0L) // ensure the outer filter is getting a boolean output
            .map(i -> {
                i.setStudStatus(Status.PASS);
                return i; // unless you return, the setter does not take effect
                })
            .count(); // terminal operation
    

    如果您可以接受稍微不同的方法并简化初始过滤器,并使用更好的方法来终止而不是 map 和 count,那么请按照以下方式进行(推荐)

        stud.stream()
            .filter(s -> {return emp.stream().anyMatch(e -> (e.id == s.id)); }) 
            .forEach(s -> s.setStatus(Status.PASS));
    

    【讨论】:

    • 不要以这种方式滥用mapcount。当大小可预测时,Java 9 的 count 实现确实已经跳过了 map 函数,未来版本可能会更进一步,当大小不可预测时,跳过 filter 不需要的 map 操作。使用forEach 执行操作是正确的方法。除此之外,并不是说不需要切换到语句 lambda 形式,.filter(s -&gt; emp.stream().anyMatch(e -&gt; (e.id == s.id))) 就可以完成这项工作。
    【解决方案4】:

    流并不比 for 循环更有效,所以使用:

        int studSize = stud.size();
        int empSize = emp.size();
        for(int i=0; i<studSize; i++) {
            for(int j=0; j<empSize; j++) {
                if (stud.get(i).getId() == emp.get(j).getId) {
                    stud.setStatus(Status.PASS);
                    break;
                }
            }
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-06-01
      • 2022-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-08
      • 2016-08-09
      相关资源
      最近更新 更多