我一直在尝试将更多的函数式编程融入到我所做的事情中,因为我编写的代码具有无副作用的性质及其在并发代码中的实用性。我遇到了过滤 Java 流的连续元素的需要,并且无法想出比普通的旧命令式方法更好的功能方法。
假设我有一个记录其参数的程序,并且我想过滤掉两个连续的元素。 例如,
-o anOption, -k aSecretKeyWhoseValueWeShouldNotLog, -a anotherOption
。 我想要的日志是-o anOption, -a anotherOption
。
我想出了几种方法,但没有一种比使用 for 循环更容易理解,该循环索引了我需要过滤掉的内容。
这似乎是一件相当普遍的事情。 是否有一种使用 Java 流或其他任何东西的模式,通常用于此类事情?
这就是我的最终结果:
static String filterSecretKeyOutOfCommandLineLogString(final String[] args) {
return joinArgsToString(filterOptionAndValueOutOfCommandLineLogString(SECRET_KEY, args));
}
private static String joinArgsToString(final String[] args) {
return Joiner.on(ARG_JOINER_DELIMITER).join(args);
}
private static String[] filterOptionAndValueOutOfCommandLineLogString(final Option option, final String[] args) {
final List<String> filteredList = new ArrayList<>();
final int numArgs = args.length;
for (int i = 0; i < numArgs; ++i) {
if (Arguments.matchesOption(option, args[i])) {
++i;
} else {
filteredList.add(args[i]);
}
}
return filteredList.toArray(new String[0]);
}
我不确定这是否比您已有的更好。假设输入是
String[]
并且您想要的(中间)输出也是 String[]
,对 Java 流的简单翻译可能是
static String[] filterOptionAndValue(String option, String args[]) {
return IntStream.range(0, args.length)
.filter(i -> i % 2 == 0)
.mapToObj(i -> new AbstractMap.SimpleEntry<>(args[i], args[i + 1]))
.filter(e -> !option.equals(e.getKey()))
.flatMap(e -> Stream.of(e.getKey(), e.getValue()))
.toArray(String[]::new);
}
public static void main(String... env) {
String[] args = {"-o", "opt1", "-k", "secret", "-a", "opt2"};
System.out.println(Arrays.toString(filterOptionAndValue("-k", args)));
}
[-o, opt1, -a, opt2]
windowFixed
收集器的解决方案:
String[] args = {
"-o", "anOption",
"-k", "aSecretKeyWhoseValueWeShouldNotLog",
"-a", "anotherOption"
};
String[] filtered = Arrays.stream(args)
.gather(Gatherers.windowFixed(2))
.filter(w -> !"-k".equals(w.getFirst()))
.flatMap(Collection::stream)
.toArray(String[]::new);
这会将值转换为成对的二元素列表的
Stream<List<String>>
。 它删除初始值为“-k”的对。 最后它会展平这些对并将它们转换回列表。