【问题标题】:Merge two text input files, each line of the files one after the other. See example合并两个文本输入文件,文件的每一行一个接一个。查看示例
【发布时间】:2017-06-26 12:59:53
【问题描述】:

我试图使用 java 8 解决一个问题,我已经使用一个简单的 for 循环解决了这个问题。但是我不知道该怎么做。 问题是:

File1 : 
1,sdfasfsf
2,sdfhfghrt
3,hdfxcgyjs

File2 : 
10,xhgdfgxgf
11,hcvcnhfjh
12,sdfgasasdfa
13,ghdhtfhdsdf

输出应该是这样的

1,sdfasfsf
10,xhgdfgxgf
2,sdfhfghrt
11,hcvcnhfjh
3,hdfxcgyjs
12,sdfgasasdfa
13,ghdhtfhdsdf

我已经基本可以正常工作了,

核心逻辑是:

List<String> left  = readFile(lhs);
List<String> right = readFile(rhs);
int leftSize = left.size();
int rightSize = right.size();
int size =  leftSize > rightSize? leftSize : right.size();
for (int i = 0; i < size; i++) {
    if(i < leftSize) {
        merged.add(left.get(i));
    }
    if(i < rightSize) {
        merged.add(right.get(i));
    }
}
  • MergeInputs.java
  • UnitTest
  • 输入文件在同一个repo的src/test/resources/com/linux/test/merge/list(只允许发布两个链接)

但是,我吹嘘我可以使用流轻松做到这一点,现在我不确定这是否可以做到。

非常感谢您的帮助。

【问题讨论】:

  • 不作为答案发布,因为它不使用流,但使用 apache commons 迭代器实用程序很容易且不易出错:List&lt;String&gt; merged = IteratorUtils.toList(IteratorUtils.zippingIterator(left.iterator(), right.iterator()));

标签: java-8 stream java-stream


【解决方案1】:

您可以简化操作以减少每个元素的条件:

int leftSize = left.size(), rightSize = right.size(), min = Math.min(leftSize, rightSize);
List<String> merged = new ArrayList<>(leftSize+rightSize);
for(int i = 0; i < min; i++) {
    merged.add(left.get(i));
    merged.add(right.get(i));
}
if(leftSize!=rightSize) {
    merged.addAll(
        (leftSize<rightSize? right: left).subList(min, Math.max(leftSize, rightSize)));
}

然后,您可以将第一部分替换为流操作:

int leftSize = left.size(), rightSize = right.size(), min = Math.min(leftSize, rightSize);

List<String> merged=IntStream.range(0, min)
        .mapToObj(i -> Stream.of(left.get(i), right.get(i)))
        .flatMap(Function.identity())
        .collect(Collectors.toCollection(ArrayList::new));
if(leftSize!=rightSize) {
    merged.addAll(
        (leftSize<rightSize? right: left).subList(min, Math.max(leftSize, rightSize)));
}

但它并不比循环变体更简单。由于其预先确定的列表,循环变体可能更有效。

将这两种操作合并到一个流操作中会更加复杂(并且可能效率更低)。

【讨论】:

  • 谢谢,听起来不错。我将研究 Function.identity() 和 flatmap 更多:) 感谢帮助
【解决方案2】:

代码逻辑应该是这样的:

int leftSize = left.size();
int rightSize = right.size();
int minSize = Math.min(leftSize,rightSize);

for (int i = 0; i < minSize; i++) {
    merged.add(left.get(i));
    merged.add(right.get(i));
}

// adding remaining elements
merged.addAll(
        minSize < leftSize ? left.subList(minSize, leftSize)
                           : right.subList(minSize, rightSize)
);

另一个选项是通过Iterator 使用切换模式,例如:

toggle(left, right).forEachRemaining(merged::add);

//OR using stream instead
List<String> merged = Stream.generate(toggle(left, right)::next)
                            .limit(left.size() + right.size())
                            .collect(Collectors.toList());

toggle 方法如下:

<T> Iterator<? extends T> toggle(List<T> left, List<T> right) {
    return new Iterator<T>() {
        private final int RIGHT = 1;
        private final int LEFT = 0;
        int cursor = -1;
        Iterator<T>[] pair = arrayOf(left.iterator(), right.iterator());

        @SafeVarargs
        private final Iterator<T>[] arrayOf(Iterator<T>... iterators) {
            return iterators;
        }

        @Override
        public boolean hasNext() {
            for (Iterator<T> each : pair) {
                if (each.hasNext()) {
                    return true;
                }
            }
            return false;
        }

        @Override
        public T next() {
            return pair[cursor = next(cursor)].next();
        }

        private int next(int cursor) {
            cursor=pair[LEFT].hasNext()?pair[RIGHT].hasNext()?cursor: RIGHT:LEFT;
            return (cursor + 1) % pair.length;
        }
    };
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多