如何在Java中实现列表折叠

Pet*_*ler 23 java collections functional-programming folding

我有一个List,并希望将其减少为单个值(函数式编程术语"折叠",Ruby术语inject),如

Arrays.asList("a", "b", "c") ... fold ... "a,b,c"
Run Code Online (Sandbox Code Playgroud)

由于我感染了函数式编程思想(Scala),我正在寻找一种更简单/更短的编码方式

sb = new StringBuilder
for ... {
  append ...
}
sb.toString
Run Code Online (Sandbox Code Playgroud)

Apo*_*isp 13

回答你原来的问题:

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; }
Run Code Online (Sandbox Code Playgroud)

F看起来像这样:

public interface F<A, B> { public B f(A a); }
Run Code Online (Sandbox Code Playgroud)

正如dfa建议的那样,Functional Java已经实现了这一点,以及更多.

例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(",")));
Run Code Online (Sandbox Code Playgroud)

例2:

import static fj.data.List.list;
import static fj.pre.Monoid.stringMonoid;
...
String abc = stringMonoid.join(list("a", "b", "c"), ",");
Run Code Online (Sandbox Code Playgroud)

例3:

import static fj.data.Stream.fromString;
import static fj.data.Stream.asString;
...
String abc = asString(fromString("abc").intersperse(','));
Run Code Online (Sandbox Code Playgroud)


Tet*_*Oni 10

特定

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();
}
Run Code Online (Sandbox Code Playgroud)

然后使用看起来像

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(); }
}
);
Run Code Online (Sandbox Code Playgroud)


And*_*son 8

如果你想将一些功能方面应用于普通的旧Java,而不需要切换语言,尽管你可以使用 LamdaJ,fork-join(166y)google-collections是帮助你添加语法糖的库.

google-collections的帮助下,您可以使用Joiner类:

Joiner.on(",").join("a", "b", "c")
Run Code Online (Sandbox Code Playgroud)

Joiner.on(",") 是一个不可变对象,因此您可以自由共享它(例如作为常量).

您还可以配置Joiner.on(", ").useForNull("nil");或处理空值处理Joiner.on(", ").skipNulls().

为了避免在生成大字符串时分配大字符串,可以使用它通过Appendable接口或StringBuilder类附加到现有的Streams,StringBuilders等:

Joiner.on(",").appendTo(someOutputStream, "a", "b", "c");
Run Code Online (Sandbox Code Playgroud)

在写出地图时,您需要两个不同的分隔符来表示键和值之间的分隔和分隔:

Joiner.on(", ").withKeyValueSeparator(":")
            .join(ImmutableMap.of(
            "today", "monday"
            , "tomorrow", "tuesday"))
Run Code Online (Sandbox Code Playgroud)


And*_*are 6

你正在寻找的是一个字符串"join"函数,遗憾的是,Java没有.您将不得不滚动自己的连接功能,这不应该太难.

编辑: org.apache.commons.lang.StringUtils似乎有许多有用的字符串函数(包括join).

  • @Jonik:我希望没有一个心智正常的人准备好为一个方法引入一个新的依赖:) (4认同)
  • 是的,但是,再一次,一个典型的项目将受益于使用来自Commons Lang或Guava等库的大量内容*.:) (4认同)

naX*_*aXa 5

您正在寻找的是join()Java自8.0以来的字符串方法.尝试以下方法之一.

  1. 静态方法String#join(delimiter, elements):

    Collection<String> source = Arrays.asList("a", "b", "c");
    String result = String.join(",", source);
    
    Run Code Online (Sandbox Code Playgroud)
  2. Stream接口支持折叠操作,与Scala的foldLeft功能非常相似.看一下下面的连接收集器:

    Collection<String> source = Arrays.asList("a", "b", "c");
    String result = source.stream().collect(Collectors.joining(","));
    
    Run Code Online (Sandbox Code Playgroud)

    您可能希望静态导入Collectors.joining以使代码更清晰.

    顺便说一下,这个收集器可以应用于任何特定对象的集合:

    Collection<Integer> numbers = Arrays.asList(1, 2, 3);
    String result = numbers.stream()
            .map(Object::toString)
            .collect(Collectors.joining(","));
    
    Run Code Online (Sandbox Code Playgroud)