如何在Java中实现列表折叠

发布于 2024-07-22 06:48:38 字数 278 浏览 5 评论 0 原文

我有一个列表,想将其减少为单个值(函数式编程术语“fold”,Ruby 术语inject),就像

Arrays.asList("a", "b", "c") ... fold ... "a,b,c"

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

sb = new StringBuilder
for ... {
  append ...
}
sb.toString

I have a List and want to reduce it to a single value (functional programming term "fold", Ruby term inject), like

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

sb = new StringBuilder
for ... {
  append ...
}
sb.toString

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

孤千羽 2024-07-29 06:48:39

您正在寻找的是一个字符串“join”函数,不幸的是,Java 没有这个函数。 您将必须推出自己的联接功能,这应该不会太难。

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

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.

Edit: org.apache.commons.lang.StringUtils seems to have many useful string functions (including join).

╭ゆ眷念 2024-07-29 06:48:39

Eclipse CollectionsinjectInto (如 Ruby 和 Smalltalk),makeStringappendString。 以下内容将适用于您的示例:

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);

注意:我是 Eclipse Collections 的提交者。

Eclipse Collections has injectInto (like Ruby and Smalltalk), makeString and appendString. The following will work with your example:

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.

疯到世界奔溃 2024-07-29 06:48:39

不幸的是,在 Java 中你无法逃脱这个循环,但是有几个库。 您可以尝试几个库:

  • lambdaj
  • 例如, org/" rel="nofollow noreferrer">functionjava
  • 特别是在您的情况下,您可以重用 我的这段代码

unfortunately in Java you can't escape that loop, there are several libraries however. E.g. you can try several libraries:

暮光沉寂 2024-07-29 06:48:39

首先,您需要一个 Java 函数库,它提供通用函子和函数投影(如折叠)。 我在这里设计并实现了一个功能强大(凭借)但简单的库: http: //www.codeproject.com/KB/java/FunctionalJava.aspx(我发现提到的其他库过于复杂)。

那么你的解决方案将如下所示:

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"

请注意,通过应用折叠,真正需要考虑的唯一部分是 Func2.call 的实现,这 3 行代码定义了一个接受累加器和一个元素并返回累加器的运算符(我的实现考虑了空字符串和空值,如果删除这种情况,那么它就会减少到 2 行代码)。

下面是 Seq.foldl 的实际实现,Seq 实现了 Iterable

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;
}

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).

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).

And here's the actual implementation of Seq.foldl, Seq implements 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;
}
烧了回忆取暖 2024-07-29 06:48:39

Java 8 风格(函数式):

// Given
List<String> arr = Arrays.asList("a", "b", "c");
String first = arr.get(0);

arr = arr.subList(1, arr.size());
String folded = arr.stream()
            .reduce(first, (a, b) -> a + "," + b);

System.out.println(folded); //a,b,c

Java 8 style (functional):

// Given
List<String> arr = Arrays.asList("a", "b", "c");
String first = arr.get(0);

arr = arr.subList(1, arr.size());
String folded = arr.stream()
            .reduce(first, (a, b) -> a + "," + b);

System.out.println(folded); //a,b,c
白馒头 2024-07-29 06:48:39

不幸的是,Java 不是一种函数式编程语言,并且没有一个好的方法来完成您想要的事情。

我相信 Apache Commons lib 有一个 名为 join 的函数 可以完成您想要的操作。

它必须足够好才能隐藏方法中的循环。

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();
}

我想你可以递归地做到这一点:

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);
}

Unfortunately Java is not a functional programming language and does not have a good way to do what you want.

I believe the Apache Commons lib has a function called join that will do what you want though.

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);
}
孤独陪着我 2024-07-29 06:48:39

现在您可以在 Java 8 中使用 String.join()

    List strings = Arrays.asList("a", "b", "c");
    String joined = String.join(",", strings);
    System.out.println(joined);

Now you can use String.join() with Java 8.

    List strings = Arrays.asList("a", "b", "c");
    String joined = String.join(",", strings);
    System.out.println(joined);
望她远 2024-07-29 06:48:39

在 lambda 的支持下,我们可以使用以下代码:

static <T, R> R foldL(BiFunction<R, T, R> lambda, R zero, List<T> theList){

     if(theList.size() == 0){
      return zero;
     }

     R nextZero = lambda.apply(zero,theList.get(0));

     return foldL(lambda, nextZero, theList.subList(1, theList.size()));                  
    }

With the support of lambdas we could do with the following code:

static <T, R> R foldL(BiFunction<R, T, R> lambda, R zero, List<T> theList){

     if(theList.size() == 0){
      return zero;
     }

     R nextZero = lambda.apply(zero,theList.get(0));

     return foldL(lambda, nextZero, theList.subList(1, theList.size()));                  
    }
街道布景 2024-07-29 06:48:39

下面是折叠列表的代码,通过保留后面的节点信息并在前进时折叠。

public class FoldList {
    public static void main(String[] args) {
        Node a = new Node(1);
        Node b = new Node(2);
        Node c = new Node(3);
        Node d = new Node(4);
        Node e = new Node(5);
        Node f = new Node(6);
        Node g = new Node(7);
        Node h = new Node(8);
        Node i = new Node(9);
        a.next = b;
        b.next = c;
        c.next = d;
        d.next = e;
        e.next = f;
        f.next = g;
        g.next = h;
        h.next = i;

        foldLinkedList(a);

    }

    private static void foldLinkedList(Node a) {
        Node middle = getMiddleNodeOfTheList(a);
        reverseListOnWards(middle);
        foldTheList(a, middle);

    }

    private static Node foldTheList(Node a, Node middle) {
        Node leftBackTracePtr = a;
        Node leftForwardptr = null;
        Node rightBackTrack = middle;
        Node rightForwardptr = null;
        Node leftCurrent = a;
        Node rightCurrent = middle.next;
        while (middle.next != null) {
            leftForwardptr = leftCurrent.next;
            rightForwardptr = rightCurrent.next;
            leftBackTracePtr.next = rightCurrent;
            rightCurrent.next = leftForwardptr;
            rightBackTrack.next = rightForwardptr;
            leftCurrent = leftForwardptr;
            leftBackTracePtr = leftCurrent;
            rightCurrent = middle.next;
        }
        leftForwardptr = leftForwardptr.next;
        leftBackTracePtr.next = middle;
        middle.next = leftForwardptr;

        return a;

    }

    private static void reverseListOnWards(Node node) {
        Node startNode = node.next;
        Node current = node.next;
        node.next = null;
        Node previous = null;
        Node next = node;
        while (current != null) {
            next = current.next;
            current.next = previous;
            previous = current;
            current = next;
        }
        node.next = previous;

    }

    static Node getMiddleNodeOfTheList(Node a) {
        Node slowptr = a;
        Node fastPtr = a;
        while (fastPtr != null) {
            slowptr = slowptr.next;
            fastPtr = fastPtr.next;
            if (fastPtr != null) {
                fastPtr = fastPtr.next;
            }
        }
        return slowptr;

    }

    static class Node {
        public Node next;
        public int value;

        public Node(int value) {
            this.value = value;
        }

    }
}

Below is the code to fold the list, by keeping back the information of the nodes let behind and folding as we move forward.

public class FoldList {
    public static void main(String[] args) {
        Node a = new Node(1);
        Node b = new Node(2);
        Node c = new Node(3);
        Node d = new Node(4);
        Node e = new Node(5);
        Node f = new Node(6);
        Node g = new Node(7);
        Node h = new Node(8);
        Node i = new Node(9);
        a.next = b;
        b.next = c;
        c.next = d;
        d.next = e;
        e.next = f;
        f.next = g;
        g.next = h;
        h.next = i;

        foldLinkedList(a);

    }

    private static void foldLinkedList(Node a) {
        Node middle = getMiddleNodeOfTheList(a);
        reverseListOnWards(middle);
        foldTheList(a, middle);

    }

    private static Node foldTheList(Node a, Node middle) {
        Node leftBackTracePtr = a;
        Node leftForwardptr = null;
        Node rightBackTrack = middle;
        Node rightForwardptr = null;
        Node leftCurrent = a;
        Node rightCurrent = middle.next;
        while (middle.next != null) {
            leftForwardptr = leftCurrent.next;
            rightForwardptr = rightCurrent.next;
            leftBackTracePtr.next = rightCurrent;
            rightCurrent.next = leftForwardptr;
            rightBackTrack.next = rightForwardptr;
            leftCurrent = leftForwardptr;
            leftBackTracePtr = leftCurrent;
            rightCurrent = middle.next;
        }
        leftForwardptr = leftForwardptr.next;
        leftBackTracePtr.next = middle;
        middle.next = leftForwardptr;

        return a;

    }

    private static void reverseListOnWards(Node node) {
        Node startNode = node.next;
        Node current = node.next;
        node.next = null;
        Node previous = null;
        Node next = node;
        while (current != null) {
            next = current.next;
            current.next = previous;
            previous = current;
            current = next;
        }
        node.next = previous;

    }

    static Node getMiddleNodeOfTheList(Node a) {
        Node slowptr = a;
        Node fastPtr = a;
        while (fastPtr != null) {
            slowptr = slowptr.next;
            fastPtr = fastPtr.next;
            if (fastPtr != null) {
                fastPtr = fastPtr.next;
            }
        }
        return slowptr;

    }

    static class Node {
        public Node next;
        public int value;

        public Node(int value) {
            this.value = value;
        }

    }
}
苍白女子 2024-07-29 06:48:39

没有这样的函数,但您可以创建如下所示的函数,并在需要时调用它。

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();

    }
}

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();

    }
}
谁与争疯 2024-07-29 06:48:38

回答你原来的问题:

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; }

F 看起来像这样:

public interface F<A, B> { public B f(A a); }

正如 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(",")));

示例 2:

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

示例 3:

import static fj.data.Stream.fromString;
import static fj.data.Stream.asString;
...
String abc = asString(fromString("abc").intersperse(','));

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:

public interface F<A, B> { public B f(A a); }

As dfa suggested, Functional Java has this implemented, and more.

Example 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:

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

Example 3:

import static fj.data.Stream.fromString;
import static fj.data.Stream.asString;
...
String abc = asString(fromString("abc").intersperse(','));
蓝眼睛不忧郁 2024-07-29 06:48:38

鉴于

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();
}

然后用法看起来就像

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(); }
}
);

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(); }
}
);
国际总奸 2024-07-29 06:48:38

如果您想将某些功能方面应用于普通的旧式 Java,而不切换语言尽管您可以 LamdaJ分叉连接 (166y)google-collections 是帮助您添加语法糖的库。

借助 google-collections,您可以使用Joiner 类

Joiner.on(",").join("a", "b", "c")

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

您还可以配置 null 处理,例如 Joiner.on(", ").useForNull("nil");Joiner.on(", ").skipNulls()

为了避免在生成大字符串时分配大字符串,您可以通过 Appendable 接口或 StringBuilder 类将其附加到现有 Streams、StringBuilder 等

Joiner.on(",").appendTo(someOutputStream, "a", "b", "c");

:编写映射时,您需要两个不同的分隔符来用于条目和键+值之间的分隔:

Joiner.on(", ").withKeyValueSeparator(":")
            .join(ImmutableMap.of(
            "today", "monday"
            , "tomorrow", "tuesday"))

If you want to apply some functional aspects to plain old Java, without switching language although you could LamdaJ, fork-join (166y) and google-collections are libraries that help you to add that syntactic sugar.

With the help of google-collections you can use the Joiner class:

Joiner.on(",").join("a", "b", "c")

Joiner.on(",") is an immutable object so you might share it freely (for example as a constant).

You can also configure null handling like Joiner.on(", ").useForNull("nil"); or 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:

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"))
岁月打碎记忆 2024-07-29 06:48:38

您正在寻找的是 Java 从 8.0 开始就有的字符串 join() 方法。 尝试以下方法之一。

  1. 静态方法 String#join(分隔符,元素)

    集合   源 = Arrays.asList("a", "b", "c"); 
      字符串结果 = String.join(",", 源); 
      
  2. Stream 接口支持与 Scala 的 foldLeft 函数非常相似的折叠操作。 看一下下面的连接 Collector

    集合   源 = Arrays.asList("a", "b", "c"); 
      String result = source.stream().collect(Collectors.joining(",")); 
      

    您可能需要静态导入Collectors.joining以使代码更清晰。

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

    集合<整数>   数字 = Arrays.asList(1, 2, 3); 
      字符串结果=numbers.stream() 
              .map(对象::toString) 
              .collect(Collectors.joining(",")); 
      

What you are looking for is a string join() method which Java has since 8.0. Try one of the methods below.

  1. Static method String#join(delimiter, elements):

    Collection<String> source = Arrays.asList("a", "b", "c");
    String result = String.join(",", source);
    
  2. Stream interface 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(","));
    
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文