如何在 Java 中实现列表折叠
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/950751/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to implement a list fold in Java
提问by Peter Kofler
I have a List and want to reduce it to a single value (functional programming term "fold", Ruby term inject
), like
我有一个 List 并希望将其减少为单个值(函数式编程术语“折叠”,Ruby 术语inject
),例如
Arrays.asList("a", "b", "c") ... fold ... "a,b,c"
As I am infected with functional programming ideas (Scala), I am looking for an easier/shorter way to code it than
由于我被函数式编程思想(Scala)感染了,我正在寻找一种比它更简单/更短的编码方式
sb = new StringBuilder
for ... {
append ...
}
sb.toString
采纳答案by naXa
What you are looking for is a string join()
method which Java has since 8.0. Try one of the methods below.
您正在寻找的是join()
Java 自 8.0 以来的字符串方法。尝试以下方法之一。
Static method
String#join(delimiter, elements)
:Collection<String> source = Arrays.asList("a", "b", "c"); String result = String.join(",", source);
Streaminterface supports a fold operation very similar to Scala's
foldLeft
function. Take a look at the following concatenating Collector:Collection<String> source = Arrays.asList("a", "b", "c"); String result = source.stream().collect(Collectors.joining(","));
You may want to statically import
Collectors.joining
to make your code clearer.By the way this collector can be applied to collections of any particular objects:
Collection<Integer> numbers = Arrays.asList(1, 2, 3); String result = numbers.stream() .map(Object::toString) .collect(Collectors.joining(","));
静态方法
String#join(delimiter, elements)
:Collection<String> source = Arrays.asList("a", "b", "c"); String result = String.join(",", source);
Stream接口支持折叠操作,非常类似于 Scala 的
foldLeft
功能。看看下面的串联Collector:Collection<String> source = Arrays.asList("a", "b", "c"); String result = source.stream().collect(Collectors.joining(","));
您可能希望静态导入
Collectors.joining
以使您的代码更清晰。顺便说一下,这个收集器可以应用于任何特定对象的集合:
Collection<Integer> numbers = Arrays.asList(1, 2, 3); String result = numbers.stream() .map(Object::toString) .collect(Collectors.joining(","));
回答by dfa
回答by Andrew Hare
What you are looking for is a string "join" function which, unfortunately, Java does not have. You will have to roll your own join function which shouldn't be too hard.
您正在寻找的是一个字符串“join”函数,不幸的是,Java 没有。您将不得不推出自己的连接功能,这应该不会太难。
Edit:org.apache.commons.lang.StringUtilsseems to have many useful string functions (including join).
编辑:org.apache.commons.lang.StringUtils似乎有很多有用的字符串函数(包括连接)。
回答by jjnguy
Unfortunately Java is not a functional programming language and does not have a good way to do what you want.
不幸的是,Java 不是一种函数式编程语言,也没有一个很好的方法来做你想做的事。
I believe the Apache Commons lib has a function called jointhat will do what you want though.
我相信 Apache Commons 库有一个名为 join的函数,它可以做你想做的事。
It will have to be good enough to hide the loop in a method.
它必须足够好才能在方法中隐藏循环。
public static String combine(List<String> list, String separator){
StringBuilder ret = new StringBuilder();
for(int i = 0; i < list.size(); i++){
ret.append(list.get(i));
if(i != list.size() - 1)
ret.append(separator);
}
return ret.toString();
}
I suppose you could do it recursively:
我想你可以递归地做到这一点:
public static String combine(List<String> list, String separator){
return recursiveCombine("", list, 0, separator);
}
public static String recursiveCombine(String firstPart, List<String> list, int posInList, String separator){
if (posInList == list.size() - 1) return firstPart + list.get(posInList);
return recursiveCombine(firstPart + list.get(posInList) + separator, list, posInList + 1, seperator);
}
回答by Tetsujin no Oni
Given
给定的
public static <T,Y> Y fold(Collection<? extends T> list, Injector<T,Y> filter){
for (T item : list){
filter.accept(item);
}
return filter.getResult();
}
public interface Injector<T,Y>{
public void accept(T item);
public Y getResult();
}
Then usage just looks like
然后用法看起来像
fold(myArray, new Injector<String,String>(){
private StringBuilder sb = new StringBuilder();
public void Accept(String item){ sb.append(item); }
public String getResult() { return sb.toString(); }
}
);
回答by Apocalisp
To answer your original question:
要回答您的原始问题:
public static <A, B> A fold(F<A, F<B, A>> f, A z, Iterable<B> xs)
{ A p = z;
for (B x : xs)
p = f.f(p).f(x);
return p; }
Where F looks like this:
F 看起来像这样:
public interface F<A, B> { public B f(A a); }
As dfa suggested, Functional Javahas this implemented, and more.
正如 dfa 所建议的,Functional Java已经实现了这一点,等等。
Example 1:
示例 1:
import fj.F;
import static fj.data.List.list;
import static fj.pre.Monoid.stringMonoid;
import static fj.Function.flip;
import static fj.Function.compose;
F<String, F<String, String>> sum = stringMonoid.sum();
String abc = list("a", "b", "c").foldLeft1(compose(sum, flip(sum).f(",")));
Example 2:
示例 2:
import static fj.data.List.list;
import static fj.pre.Monoid.stringMonoid;
...
String abc = stringMonoid.join(list("a", "b", "c"), ",");
Example 3:
示例 3:
import static fj.data.Stream.fromString;
import static fj.data.Stream.asString;
...
String abc = asString(fromString("abc").intersperse(','));
回答by Andreas Petersson
If you want to apply some functional aspects to plain old Java, without switching language although you couldLamdaJ, fork-join (166y)and google-collectionsare libraries that help you to add that syntactic sugar.
如果您想将一些功能方面应用于普通的旧 Java,而无需切换语言,尽管您可以使用LamdaJ,fork-join (166y)和google-collections是帮助您添加语法糖的库。
With the help of google-collectionsyou can use the Joiner class:
在google-collections的帮助下,您可以使用Joiner 类:
Joiner.on(",").join("a", "b", "c")
Joiner.on(",")
is an immutable object so you might share it freely (for example as a constant).
Joiner.on(",")
是一个不可变对象,因此您可以自由共享它(例如作为常量)。
You can also configure null handling like Joiner.on(", ").useForNull("nil");
or Joiner.on(", ").skipNulls()
.
您还可以配置空处理,如Joiner.on(", ").useForNull("nil");
或Joiner.on(", ").skipNulls()
。
To avoid allocating big strings while you are generating a large string, you can use it to append to existing Streams, StringBuilders, etc. through the Appendable
interface or StringBuilder
class:
为了避免在生成大字符串时分配大字符串,您可以使用它通过Appendable
接口或StringBuilder
类附加到现有的 Streams、StringBuilders 等:
Joiner.on(",").appendTo(someOutputStream, "a", "b", "c");
When writing out maps, you need two different separators for entries and seperation between key+value:
写出映射时,您需要两个不同的分隔符用于条目和键+值之间的分隔:
Joiner.on(", ").withKeyValueSeparator(":")
.join(ImmutableMap.of(
"today", "monday"
, "tomorrow", "tuesday"))
回答by Stephen Swensen
First you'll need a functional library for Java which supplies generic functors and functional projections like fold. I've designed and implemented a powerful (by virtue) yet simple such library here: http://www.codeproject.com/KB/java/FunctionalJava.aspx(I found the other libraries mentioned overly complicated).
首先,您需要一个 Java 函数库,它提供通用函子和函数投影(如 fold)。我在这里设计并实现了一个强大的(凭借优点)但简单的库:http: //www.codeproject.com/KB/java/FunctionalJava.aspx(我发现提到的其他库过于复杂)。
Then your solution would look like:
那么您的解决方案将如下所示:
Seq.of("","a",null,"b","",null,"c","").foldl(
new StringBuilder(), //seed accumulator
new Func2<StringBuilder,String,StringBuilder>(){
public StringBuilder call(StringBuilder acc,String elmt) {
if(acc.length() == 0) return acc.append(elmt); //do not prepend "," to beginning
else if(elmt == null || elmt.equals("")) return acc; //skip empty elements
else return acc.append(",").append(elmt);
}
}
).toString(); //"a,b,c"
Note that by applying fold, the only part that really needs to be thought out is the implementation for Func2.call, 3 lines of code which define an operator accepting the accumulator and an element and returning the accumulator (my implementation accounts for empty strings and nulls, if you remove that case then it's down to 2 lines of code).
请注意,通过应用折叠,真正需要考虑的唯一部分是 Func2.call 的实现,3 行代码定义了一个接受累加器和元素并返回累加器的运算符(我的实现占空字符串和nulls,如果你删除那个案例,那么它就会减少到 2 行代码)。
And here's the actual implementation of Seq.foldl, Seq implements Iterable<E>:
下面是 Seq.foldl 的实际实现,Seq 实现了 Iterable<E>:
public <R> R foldl(R seed, final Func2<? super R,? super E,? extends R> binop)
{
if(binop == null)
throw new NullPointerException("binop is null");
if(this == EMPTY)
return seed;
for(E item : this)
seed = binop.call(seed, item);
return seed;
}
回答by OscarRyz
There is no such a function, but you could create something like the following, and invoke it whenever you need to.
没有这样的函数,但您可以创建如下所示的内容,并在需要时调用它。
import java.util.Arrays;
import java.util.List;
public class FoldTest {
public static void main( String [] args ) {
List<String> list = Arrays.asList("a","b","c");
String s = fold( list, ",");
System.out.println( s );
}
private static String fold( List<String> l, String with ) {
StringBuilder sb = new StringBuilder();
for( String s: l ) {
sb.append( s );
sb.append( with );
}
return sb.deleteCharAt(sb.length() -1 ).toString();
}
}
回答by Donald Raab
Eclipse Collectionshas injectInto (like Ruby and Smalltalk), makeString and appendString. The following will work with your example:
Eclipse Collections有injectInto(如Ruby 和Smalltalk)、makeString 和appendString。以下将适用于您的示例:
String result1 = FastList.newListWith("a", "b", "c").makeString(",");
StringBuilder sb = new StringBuilder();
FastList.newListWith("a", "b", "c").appendString(sb, ",");
String result2 = sb.toString();
Assert.assertEquals("a,b,c", result1);
Assert.assertEquals(result1, result2);
Note: I am a committer for Eclipse Collections.
注意:我是 Eclipse Collections 的提交者。