Java 8流可以成对进行处理

时间:2014-07-08 22:29:36

标签: java java-8 java-stream

只是踢了Java 8的轮胎,并注意到Lamdas和流功能编程。

想知道一个简单的命令行args消费者是否可以使用流。

无法弄清楚如何同时获得两个流元素然而...... 除此之外我还需要处理那些不会接受值的args,所以不能做任何奇怪/偶数的诡计。会考虑args始终以破折号开头,而可选值永远不会。

String[] args = ("-v", "-c", "myconfigfile", "-o", "outputfile");

Arrays.toList(args).stream().map( a,v -> evalArg(a,v));

public static void evalArg(String arg, String val) {
    switch(arg) {
        case "-v":
            verbose = true;
            break;
        case "-c":
            config_file = val;
            break;
        case "-o":
            output_file = val;
            break;
        default:
            System.err.println("unknown argument " + arg + " " + val);
            break;
    }
}

5 个答案:

答案 0 :(得分:2)

如果您有键值对,则可以使用以下内容:

public static void main(final String[] args) {
    String[] args = {"-v", "value", "-c", "myconfigfile", "-o", "outputfile"};

    pairStream(Arrays.asList(args), (param, value) -> param + ": " + value)
        .forEach(System.out::println);
}

public static <X, Y> Stream<Y> pairStream(List<X> list, BiFunction<X, X, Y> mapper) {
    Supplier<X> s = list.iterator()::next;
    return Stream.generate(() -> mapper.apply(s.get(), s.get()))
        .limit(list.size() / 2);
}

// Result:
//    -v: value
//    -c: myconfigfile
//    -o: outputfile

答案 1 :(得分:1)

Stream API实际上并不是为了处理依赖于另一个值的状态的值而构建的。使用的lambda函数应该是无状态的(另请参阅Java文档:Stream#map())。但是,可以在流上使用sequential()来确保正确的顺序,并允许在用于处理元素的函数内部使用状态,但这并不是真的建议。

更好地使用库进行参数解析,例如Apache Commons CLI

答案 2 :(得分:0)

使用lambdas / mapping没有简单的方法。并非所有形式的迭代都适合映射单个lambda。

例如,对此的功能方法可能不会使用lambdas;它将使用递归,如果需要,每个步骤可选择弹出更多args。像(伪代码,忽略错误检查等):

Options[] parse(args : String[]):
    if args.isEmpty:
        return []
    currentOpt, remainingArgs = args.pop()
    case currentOpt of:
        "-v":
            return [ Verbose ] ++ parse(remainingArgs)
        "-c":
            configPath, remainingArgs = remainingArgs.pop()
            return [ Config(configPath) ] ++ parse(remainingArgs)
        "-o":
            outputPath, remainingArgs = remainingArgs.pop()
            return [ Output(outputPath) ] ++ parse(remainingArgs)
        _:
            return [ UnknownOption(currentOpt) ] ++ parse(remainingArgs)

答案 3 :(得分:0)

在写完我的原始答案之后,我意识到这可以使用reduce,例如:

String[] foo = {"-t", "-c", "myconfigfile", "-o", "outputfile"};

Arrays.stream(foo).reduce((arg, val) -> {
    switch (arg) {
        case "-v":
            verbose = true;
            break;
        case "-c":
            configFile = val;
            break;
        case "-o":
            outputFile = val;
            break;
        // Non-exhaustive
    }
    return val;
});

我的原始答案,使用状态和功能对象:

请记住,Java实际上并不支持第一类函数。当您使用lambda时,您实际上正在传递包含函数的对象。这些对象可以具有状态。你可以利用这个优势,所以你应该能够做到这样的事情:

String[] foo = {"-c", "myconfigfile", "-o", "outputfile"};

Arrays.stream(foo).forEachOrdered(new Consumer<String>() {
    String last;

    public void accept(String t) {
        if (last == null) {
            last = t;
        } else {
            System.out.println(last + " " + t);
            last = null;
        }
    }
});

这是否是一个好主意是一个不同的考虑因素。请注意,使用forEachOrdered作为普通的forEach不保证按特定顺序通过列表。还要注意,map不适用于使用两个元素做一件事,因为映射函数必须接受一个参数并返回一个结果,从而导致输入流和输出流之间的一对一关系。

对于您的具体示例,您必须执行以下操作:

String[] foo = {"-t", "-c", "myconfigfile", "-o", "outputfile"};

Arrays.stream(foo).forEachOrdered(new Consumer<String>() {
    String arg;

    public void accept(String val) {
        if (arg == null) {
            arg = val;
        } else if (t.startsWith("-")) {
            System.out.println(arg);
            arg = val;
        } else {
            System.out.println(arg + " " + val);
            arg = null;
        }
    }
});

第三种选择当然是yshavit suggests,而根本不使用流:

LinkedList<String> argList = LinkedList<>(Arrays.asList(args));

while(!argList.isEmpty()) {
    switch (argList.pop()) {
        case "-v":
            verbose = true;
            break;
        case "-c":
            configFile = argList.pop();
            break;
        case "-o":
            outputFile = argList.pop();
            break;
        default:
            System.err.println("unknown argument " + arg + " " + val);
            break;
    }
}

答案 4 :(得分:0)

你似乎有这个想法,但我想我会用一些额外的数据扩展它。

数据流的概念可能比Java早;处理它们至少可以追溯到处理器架构。 CPU采用Von Neumann Architecture构建;它允许处理器维护指向内存不同部分的指针,并且基本上可以一直访问所有内存。常用的一些其他处理器(例如GPU)是流处理器。它们一次处理一个操作,除了偶尔(通常是可疑的)聪明的技巧之外,不知道流的任何其他部分。这样就可以实现最佳的并行处理,这正是为什么GPGPU可以对某些任务起到极大的效果,但在运行整个机器时却非常无效。

Streams API允许您在相同的情况下运行操作。一次操作多个项目的唯一方法是通过减少;这基本上是另一个流的构建。通过它,理论上,你当然可以扫描参数和启动选项;但由于潜在的冗余,这并不意味着它在实践中是最好的。