Java 8 Stream - 如何使用一个项目列表替换字符串内容

12

我希望用Java8的 .stream() 或者 .foreach() 替换下面的代码。但是我做起来有困难。

这可能很简单,但是我发现函数式编程的方式很难 :)

我可以遍历,没有问题,但由于可变性问题,返回修改后的字符串是个问题。

有人有什么想法吗?

List<String> toRemove = Arrays.asList("1", "2", "3");
String text = "Hello 1 2 3";

for(String item : toRemove){
    text = text.replaceAll(item,EMPTY);
}

谢谢!

5个回答

36

由于您无法使用流来修改text变量,因此您必须将操作强制转换为一个Function,然后应用于text以获得最终结果:

List<String> toRemove = Arrays.asList("1", "2", "3");
String text = "Hello 1 2 3";
text=toRemove.stream()
             .map(toRem-> (Function<String,String>)s->s.replaceAll(toRem, ""))
             .reduce(Function.identity(), Function::andThen)
             .apply(text);

2
很棒,我以前从未见过Function.identity(),Function::andThen的使用。干得好。 - SteveG
@Holger 或者(就像你之前向我展示的那样):.reduce(Function::andThen).orElse(Function.identity()) 1+ - Eugene
我还不是很理解,但这对我有用。谢谢! - Grzech

16

哇,你们喜欢用麻烦的方法。这就是 filter() 和 collect() 的用处。

List<String> toRemove = Arrays.asList("1", "2", "3");
String text = "Hello 1 2 3";

text = Pattern.compile("").splitAsStream(text)
    .filter(s -> !toRemove.contains(s))
    .collect(Collectors.joining());
System.out.println("\"" + text + "\"");

输出(与原始代码相同)

"Hello   "

当然,如果您的搜索字符串长度超过一个字符,则前面的方法更有效。但是,如果您有一个分词字符串,则拆分和连接更容易。

List<String> toRemove = Arrays.asList("12", "23", "34");
String text = "Hello 12 23 34 55";
String delimiter = " ";

text = Pattern.compile(delimiter).splitAsStream(text)
    .filter(s -> !toRemove.contains(s))
    .collect(Collectors.joining(delimiter));
System.out.println("\"" + text + "\"");

输出

"Hello 55"

太好了!那看起来是目前最好的解决方案。我现在开始掌握要领了。干得好! - SteveG
5
除了通过String.split创建一个暂时的数组持有所有元素,这与使用Stream API恰好相反。如果使用Pattern.compile(delimiter).splitAsStream(text),那么这将是一个很好的答案,虽然不完全符合问题的要求,但无妨。 - Holger
如果你有一个要删除的项目超过几个,将它们存储为Set<String>而不是List<String>会显著提高效率,因为在Set上使用.contains()的效率更高。 - Steve K
你的回答只适用于单个字符或者有分隔符的情况。Holger的回答更通用(你的回答对我来说不适用)。 - undefined

10
text = toRemove.stream()
               .reduce(text, (str, toRem) -> str.replaceAll(toRem, ""));

适合你的工作。


4
您希望我翻译的内容是:“你能否在这段代码中添加一些解释,说明为什么这段代码可以回答这个问题?因为只有代码本身的回答是不被鼓励的,它们无法传授解决方案。” - Nathan Tuggy
如果流是并行的,这将无法工作。 - Dimitar Spasovski

2
如果我理解正确,您想要做类似这样的事情:
toRemove.forEach(removeString -> {
    text = text.replaceAll(removeString, "");
});

唯一的问题是,你不能这样做。:(
你可以在这里阅读相关内容:http://javarevisited.blogspot.co.il/2014/02/10-example-of-lambda-expressions-in-java8.html 第6部分:lambda表达式的限制是,你只能引用final或有效的final局部变量,这意味着你不能修改在lambda外部作用域中声明的变量。 编辑 你可以做一些非常丑陋的事情。像这样:
private static String text;

public void main (String[] args) {
    text = "Hello 1 2 3";
    List<String> toRemove = Arrays.asList("1", "2", "3");
    toRemove.forEach(removeString -> replaceTextWithEmptyString(removeString));
}

private static void replaceTextWithEmptyString(String whatToReplace) {
    text = text.replaceAll(whatToReplace, "");
}

这就是我得到的,我想知道是否有其他方法来解决它。感谢您的回复。 - SteveG
@SteveG 请查看我的编辑。但请记住,这个解决方案非常丑陋。 - Michael
很酷,它还分离了关注点,如果这是一个复杂的方法,那就太好了。 - SteveG

0
List<String> versions = new ArrayList<>();
    versions.add("Lollipop");
    versions.add("KitKat");
    versions.add("Jelly Bean");
    versions.add("Ice Cream Sandwidth");
    versions.add("Honeycomb");
    versions.add("Gingerbread");

    System.out.println("Data");
    versions.stream().filter(s -> s.startsWith("G")).map(i->i.replace("G", "bn")).forEach(System.out::println);

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接