【问题标题】:Convert the normal old 'for loop' to Java 8 'forEach'将普通的旧“for循环”转换为Java 8“forEach”
【发布时间】:2020-12-11 09:30:30
【问题描述】:

见下文,我正在使用旧的 for 循环和如此多的 if 条件,我需要您的支持才能将其转换为 Java 8 forEach。我很无力解决这个问题。

在这里,我使用旧的 for 循环迭代订单对象,一次将 500 条数据发送到第三方服务器。 因为第三方服务器当时只允许 1500 行数据。所以我根据限制 1500 拆分列表。

这只是我的独立课程。我的意图是将旧的 for 循环转换为 java 8 流。

【问题讨论】:

  • 为什么你想用forEach替换它
  • 使用流不会神奇地使代码更具可读性,当然也不会更快。实现可读性的第一步是重构您的代码,以避免有 3 个不同的复制粘贴块调用 sendToCarwings
  • 在重构此类代码时,最好从最内层开始。首先将相同的块提取到一个方法中。
  • @tucuxi 确实 - 我对 OP 的建议是将代码重新转换为 first 处理将完整请求分解为 List<List<Orders>> 的形式,其中每个列表是最多 500 个元素。然后他们可以使用 range-for 来处理并发送每个请求。
  • 这只是我的独立类,实际上,我已经用记录器优化了代码,命令方法一切。我的意图是将旧的 for 循环转换为 java 8 流。

标签: java java-stream


【解决方案1】:

在这种情况下,我建议不要使用 Streams 选项,因为您想要的基本上与“flatMap”相反,即取一个 X 流并返回一个 Y 流,每个流都由几个 X。

在这个独立的示例中,chunkList 函数将列表“拆分”为块,而不进行复制(因为它只使用 subList),然后您的代码可以处理它,与分块代码分开 - 这可能进入sendRequest 函数,如果大小对于单个请求来说太大,我只是通过抛出来模拟它。我认为这充分代表了您的原始问题,但如果不是,请告诉我。

同样,您基本上可以“忽略”我的 Ordersrepo_findAll 实现,它们基本上是为了使示例独立且可测试的模拟。

import java.util.List;

public class App 
{
    /** Your request size limit */
    private static final int MAX_REQ_SIZE = 500;

    /** Dummy representing your code that makes the HttpRequest. */
    public static void sendRequest(List<Orders> data) {
        if (data.size() > MAX_REQ_SIZE)
            throw new IllegalArgumentException("Invalid request size");
        System.out.println("Sending request of size " + data.size());
    }

    /** Possibility without Stream shenanigans. This function just creates a list of sub-lists of its argument,
     * each with a size &le; the given chunk size. The returned value will:<ul>
     * <li>Be non-null, with maybe 0 elements if the original list is empty.</li>
     * <li>Have elements of size chunkSize for every element except the last</li>
     * <li>Ultimately contain just references to indices in the original list, in the same order</li>
     * </ul>
     */
    public static List<List<Orders>> chunkList(List<Orders> orig, final int chunkSize) {
        if (chunkSize <= 0)
            throw new IllegalArgumentException();
        final List<List<Orders>> ret = new java.util.ArrayList<>();
        int begin = 0;
        for (; begin + chunkSize <= orig.size(); begin += chunkSize) {
            ret.add(orig.subList(begin, begin + chunkSize));
        }
        if (begin < orig.size()) {
            ret.add(orig.subList(begin, orig.size()));
        }
        return ret;
    }
    
    public static void main( String[] args )
    {
        // Your original dataset
        final List<Orders> data = repo_findAll();

        // The resulting operation
        for (List<Orders> curChunk : chunkList(data, MAX_REQ_SIZE)) {
            // You could place exception handling here to retry a chunk if it fails, etc
            sendRequest(curChunk);
        }
    }

    
    /** Dummy data class */
    private static class Orders {
        public final int number;
        public Orders(int number) {
            this.number = number;
        }
        @Override public String toString() {
            return "Order #" + number;
        }
    }
    /** Dummy representing your orders-generating function. This one makes a list of random size ~0.5-3 times the max request size, per sizesRnd below */
    static List<Orders> repo_findAll() {
        final int genSize = sizesRnd.nextInt();
        return java.util.stream.IntStream.range(1,genSize+1)
                .mapToObj(Orders::new).collect(java.util.stream.Collectors.toList());
    }
    private static final java.util.PrimitiveIterator.OfInt sizesRnd = new java.util.Random().ints(MAX_REQ_SIZE/2, MAX_REQ_SIZE*3+20).iterator();
}

【讨论】:

  • 谢谢哈维尔
【解决方案2】:

如果我理解您的问题,您正在尝试将一个列表拆分为几个给定大小的子列表。我不会在迭代期间确定子列表,而是事先执行此操作,然后对子列表执行相同的进一步处理,即

  1. 从您的订单列表List&lt;Order&gt; =&gt; List&lt;List&lt;Order&gt;&gt; 创建一个大小为 500 的订单列表列表
  2. 遍历上述列表并一次发送一个列表的请求

例子

List<Orders> order = (List<Orders>) repo.findAll();
int chunkSize = 500;
AtomicInteger ai = new AtomicInteger();

Collection<List<Orders>> chunkedOrders = order.stream()
    .collect(Collectors.groupingBy(item -> ai.getAndIncrement() / chunkSize))
    .values();

//then the rest of your logic to send the requests
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);

chunkedOrders.forEach(chunk -> {    
    HttpEntity<List<Orders>> requestEntity = new HttpEntity<List<Orders>>(chunk, headers);
    RestTemplate restTemplate = new RestTemplate();
    System.out.println("Sending" + " " + chunk.size() + " " + "order data");
    sendToCarwings(restTemplate, requestEntity);
    System.out.println("Successfully sent" + " " + chunk.size() + " " + "order data");
 });

【讨论】:

  • 谢谢你,@厄立特里亚。我需要检查性能吗?
  • @sibinRasiya 性能可能取决于除上述 sn-p 之外的许多因素。将此与您的原始实现进行比较。如果您发现负显着差异,可能值得仔细研究。
  • 我测试了99999条数据,性能非常好。谢谢@Eritrean,我的问题已解决
  • 我用原始实现测试了上面的sn-p。再次完美运行谢谢老板
  • @sibinRasiya 欢迎您。很高兴我能帮上忙。
【解决方案3】:

循环改变了 minLimit 和 maxLimit,所以 forEach 是不够的。

但您可以按如下方式改进循环:

    final int fixed = 500;
    int minLimit = 0;
    int maxLimit;
    for (maxLimit = fixed; maxLimit <= order.size(); maxLimit += fixed) {
            List<Orders> orderSubList = order.subList(minLimit, maxLimit);
            HttpEntity<List<Orders>> requestEntity = new HttpEntity<List<Orders>>(orderSubList, headers);
            RestTemplate restTemplate = new RestTemplate();
            System.out.println("Sending" + " " + minLimit + " " + "to" + " " + maxLimit + " " + "order data");
            sendToCarwings(restTemplate, requestEntity);
            System.out
                    .println("Successfully sent" + " " + minLimit + " " + "to" + " "+maxLimit + " " + "order data");
            minLimit = maxLimit;
    }

以不同的方式处理订单列表可能更有效;内存使用等。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-30
    • 2018-01-19
    相关资源
    最近更新 更多