循环时增强和删除冗余长度检查

时间:2017-03-24 14:13:51

标签: java loops

我有一段代码如下:

 while ((line = reader.readNext()) != null) {

                identityJmPojo.setIdentity(line[0]);
                identityJmPojo.setJM(line.length  > 1 ? line[1] : jsonValue);

                identityJmList.add(identityJmPojo);
                size = identityJmList.size();

                switch (size) {
                    case STEP:
                        counter = counter + STEP;
                        payloadEngine.prepareJson(identityJmList, uploaderPoolService);
                        identityJmList.clear();

                        long stopTime = System.currentTimeMillis();
                        long elapsedTime = stopTime - startTime;
                        logger.info("=================== Time taken to read " + STEP + " records from CSV: " + elapsedTime + " and total records read: " + counter + "===================");
                        break;
                }

            }

我正在从CSV中读取数千亿条记录,速度对业务至关重要。

现在声明:

identityJmPojo.setJM(line.length  > 1 ? line[1] : jsonValue);

业务定义的考虑因素是line.length是否>在第一次迭代中,对于第n次迭代,它将是相同的,因此我想在每次循环迭代中删除冗余条件检查。只是以某种方式设置这一次,再也不使用三元运算符。

任何提示或头脑都会非常感激。

2 个答案:

答案 0 :(得分:2)

首先我应该注意,除非setJM()方法特别昂贵,否则这看起来不是优化的有力候选者。在分析器中运行您的代码,找出它真正花费时间的地方,然后再决定是否适合您的工作。

只猜测你正在做的处理类型 - 但如果瓶颈是磁盘IO,我不会感到惊讶。时间cat inputfile > /dev/null - 这将让您了解简单地从磁盘上获取字节所需的时间(但要小心解释这一点,因为文件系统内存缓存会使问题混淆)。同样,最简单的程序生成模拟输出数据,写入磁盘。

两个非常简单的选项,它们非常明显:

在您进入循环之前处理第一行

 line = reader.readNext();
 if(line != null) {
     stuffToDoOnlyOnFirstLine(...);
 }
 while(line != null) {
     stuffToDoOnAllLines(...);
     line = reader.readNext();
 }

拥有两个reader.readNext()可能会感觉很狡猾,但这是一个完善的模式,称为“预读,直读”。

标记表示作业已完成

 boolean processedFirstLine = false;

 while(...) {
      ...
      if(!processedFirstLine) {
           stuffToDoOnFirstLine(...);
           lineLengthNoted = true;
      }
      ...
 }

通过将功能放入类中,您可以获得更多的OO:

   class OnceOnlyThingDoer {
        private boolean done = false;

        public OnceOnlyThingDoer(...) {
            // set member variables e.g. the target POJO
        }

        public void record(int[] line) {
            if(!done) {
                doTheThing(line); // e.g. call your method on the target POJO
                done = true;
            }

        }
   }

你可以使用lambdas做类似的整洁事情,但我想如果你对lambdas感到满意,你就不会问这个问题。

@Test
public void writesOnlyOnce() {
    List<Integer> output = new ArrayList<>();

    Consumer<Supplier<Integer>> consumer = consumeOnlyOnce(num -> output.add(num));

    consumer.accept(() -> 5); // body of supplier could be much more complex
    consumer.accept(() -> 3);

    assertThat(output, is(Collections.singletonList(5)));

}

public Consumer<Supplier<Integer>> consumeOnlyOnce(Consumer<Integer> handler) {
    final boolean[] done = new boolean[] { false };

    return supplier -> {
        if (!done[0]) {
            handler.accept(supplier.get());
        }
        done[0] = true;
    };
}

这里的关键是我们传递Supplier函数。它的主体可能很复杂且运行起来很昂贵,但它只在supplier.get()被调用时运行,由done保护。不幸的是,在Java中我们不能在lambda中使用非最终的超出范围的变量,所以为了跟踪状态我们需要使用可变的东西,因此是单元素int[]数组。

答案 1 :(得分:-1)

你可以使用do - while循环:

    line = reader.readNext();
    boolean useLine = line.length > 1;
    do {

        identityJmPojo.setIdentity(line[0]);
        identityJmPojo.setJM(useLine ? line[1] : jsonValue);
        ....
        line = reader.readNext();
    } while(line != null);