【问题标题】:Convert for loop to forEach Lambda将 for 循环转换为 forEach Lambda
【发布时间】:2025-05-04 22:10:01
【问题描述】:

我正在学习 Lambda,但在转换时遇到了一点困难。我需要引入一个 List,使用类 Arrays 的 asList 方法将 Field 类的 values 方法提供的数组复制到其中。然后我需要使用 lambda 表达式作为参数将 for 循环转换为 forEach 内部循环。 lambda 表达式的主体将是当前 for 循环主体的代码。我相信我的 List 语法是正确的( List list = Arrays.asList(data); ),但我很难弄清楚如何处理 for 循环,甚至从哪里开始。任何指导将不胜感激。谢谢

public AreaData(String... data)
{
    List<String> list = Arrays.asList(data);


    /* Assert to check that the data is of the expected number of items. */
    assert data.length == Field.values().length : "Incorrect number of fields";

    for( Field field : Field.values() )
    {
        int width;
        String formatString;
        if( field == NAME )
        {
            /* Get the name value and store it away. */
            String value = data[field.position()];
            strings.put(field, value);
            /* Get the needed width of the field to hold the name. */
            width = max(value.length(), field.getFieldHeading().length());
            formatString = "s";
        } else
        {
            /* If the value is of the wrong form, allow the NumberFormatException
               to be thrown. */
            Double value = Double.parseDouble(data[field.position()]);
            /* Assertion to check value given is positive.  */
            assert value.compareTo(0.0) >= 0 :
                    "invalid " + field.name() + " value=" + value.toString();
            /* Get the field value and store it away. */
            doubles.put(field, value);
            /* Get needed width of the field to hold the heading or value. */
            width = max((int) log10(value) + MINIMUM,
                    field.getFieldHeading().length() + HEADING_SEPARATION);
            formatString = ".2f";
        }
        /* Keep the widest value seen, and record the corresponding format. */
        if( width > WIDTHS.get(field) )
        {
            WIDTHS.put(field, width);
            FORMATS.put(field, "%" + width + formatString);
        }
    }
    /* Optimization: to avoid doing this every time a comparison is made. */
    this.nameCaseless = strings.get(NAME).toUpperCase().toLowerCase();
}

【问题讨论】:

  • 据我所知,这里没有理由使用 lambda。

标签: java arrays lambda java-8


【解决方案1】:

Stream.of(Field.values()).forEach() 应该可以解决问题:

public AreaData (String... data) {
        List<String> list = Arrays.asList(data);
        /* Assert to check that the data is of the expected number of items. */
        assert data.length == Field.values().length : "Incorrect number of fields";
        int width;
        String formatString;
        Stream.of(Field.values()).forEach(
                field -> {
                    if (field == NAME) {
                        /* Get the name value and store it away. */
                        String value = data[field.position()];
                        strings.put(field, value);
                        /* Get the needed width of the field to hold the name. */
                        width = max(value.length(), field.getFieldHeading().length());
                        formatString = "s";
                    } else {
                        /* If the value is of the wrong form, allow the NumberFormatException
                           to be thrown. */
                        Double value = Double.parseDouble(data[field.position()]);
                        /* Assertion to check value given is positive.  */
                        assert value.compareTo(0.0) >= 0 :
                                "invalid " + field.name() + " value=" + value.toString();
                        /* Get the field value and store it away. */
                        doubles.put(field, value);
                        /* Get needed width of the field to hold the heading or value. */
                        width = max((int) log10(value) + MINIMUM,
                                field.getFieldHeading().length() + HEADING_SEPARATION);
                        formatString = ".2f";
                    }
                    /* Keep the widest value seen, and record the corresponding format. */
                    if (width > WIDTHS.get(field)) {
                        WIDTHS.put(field, width);
                        FORMATS.put(field, "%" + width + formatString);
                    }
                });

        /* Optimization: to avoid doing this every time a comparison is made. */
        this.nameCaseless = strings.get(NAME).toUpperCase().toLowerCase();
    }

也就是说,您应该考虑以下经验法则:

理想情况下,一个 lambda 表达式应该最多 3 行代码,并且不 大小写超过 5 行!

【讨论】:

  • @TheDude76 我没有看到对List&lt;String&gt; list = Arrays.asList(data); 中定义的list 的任何引用,所以我怀疑这一行可以删除。
  • 这个经验法则从何而来?在我的书中,理想情况下,lambda 表达式应该适合合理长度的一行。
  • @StuartMarks 这就是我使用“最多”的原因。一行是终极的,但 3 行仍然非常可读,例如 CheckPersonEligibleForSelectiveService 示例:docs.oracle.com/javase/tutorial/java/javaOO/…
  • @StuartMarks 很有趣,我刚刚收藏了你最后的一条推文(可能或多或少在你评论这个答案的同时:)))
  • @alfasin 啊哈,原来是你!我花了一段时间来破解你的代码。 :-)
【解决方案2】:

如果您特别想将其转换为使用流和 lambda,那么我认为您还应该抓住机会根据这些工具的意图对其进行重构。这意味着使用过滤器、收集器等,而不仅仅是将所有代码转换为单个 lambda。

例如:

Arrays.stream(Field.values())
    .peek(field -> field.storeValue(data))
    .filter(field -> field.getWidth(data) > widths.get(field))
    .forEach(field -> storeWidthAndFormat(data, widths, formats));

这假设您将与 NAME 关联的逻辑封装在 Field 枚举中(这是我的建议)。

【讨论】: