Java 使用参数化流跟踪迭代

Java 使用参数化流跟踪迭代,java,lambda,java-8,java-stream,Java,Lambda,Java 8,Java Stream,此代码: Kid[] kids = Kid.getSimpleArray(); String names = Stream.of(kids) .filter(Kid::hasToy) .map(Kid::getSurname) .collect(Collectors.joining(", ")); out.println(names); 给我这个结果: 圣比昂、英格洛、祖布沃纳、勒皮耶、阿戈夫斯卡

此代码:

Kid[] kids = Kid.getSimpleArray();
String names = Stream.of(kids)
                .filter(Kid::hasToy)
                .map(Kid::getSurname)
                .collect(Collectors.joining(", "));
out.println(names);
给我这个结果:

圣比昂、英格洛、祖布沃纳、勒皮耶、阿戈夫斯卡

我在想一个解决办法,让它变成这样:

public static<T> Function<T, String> getCountedToStringFunc(){
    AtomicInteger i = new AtomicInteger(0);
    return t -> i.incrementAndGet() + ". " + t.toString();
}
myStream
.filter(...)
.map(getCountedToStringFunc())
.collect(Collectors.joining(",")));
1.Stępień,2.Inglot,3.祖布沃纳,4.Lepiej,5.Łagowska

注:我想不出比这更好的问题来得到我想要的答案。

请注意,这真的很难看,我不应该在这里给出建议,但它就在这里。

您可以使用临时托收,作为柜台。这样做的好处是,我们可以将引用阻止为final,但仍然可以向其添加元素

每次
Kid
通过过滤器时,我们都会向它添加任何内容,并获取字符串中整数的大小

final List tmp = new ArrayList();
Kid[] kids = Kid.getSimpleArray();
String names = Stream.of(kids)
                .filter(Kid::hasToy)
                .peek(x -> tmp.add(1))
                .map(x -> tmp.size() + "." + x.getSurname())
                .collect(Collectors.joining(", "));
out.println(names);

编辑 下面是@Sam和@Tunaki提供的另外两个解决方案


@萨姆
@图纳基
流一次只能对一个项目进行操作。每个计算只知道它所操作的元素。因此,尝试添加一些关于物品在订单中的位置的知识总是一个难题

收集到
列表中
而不是字符串将允许您对所有项的列表进行最终计算,而不是单独对单个项进行计算。下面是一个方法,它接受
列表
,并将其格式化为以逗号分隔的
字符串
编号项目:

static String enumerateAndJoin(List<String> l, String separator) {
    StringBuffer sb = new StringBuffer();
    ListIterator<String> it = l.listIterator();
    while(it.hasNext()) {
        sb.append(it.nextIndex() + 1).append(".").append(it.next());
        if (it.hasNext()) {
            sb.append(separator);
        }
    }
    return sb.toString();
}
对于一种既省去一两行代码又更加清晰的混合方法,我喜欢番石榴的
fluentiable

int i = 0;
Iterator<Kid> kidsWithToys = FluentIterable.of(kids).filter(Kid::hasToy).iterator();
StringBuffer sb = new StringBuffer();
while (kidsWithToys.hasNext()) {
    sb.append(++i).append(".").append(kidsWithToys.next().getSurname());
    if (kidsWithToys.hasNext()) {
        sb.append(", ");
    }
}
inti=0;
迭代器kidsWithToys=fluentiable.of(kids).filter(Kid::hasToy).Iterator();
StringBuffer sb=新的StringBuffer();
while(kidsWithToys.hasNext()){
sb.append(++i).append(“.”).append(kidsWithToys.next().getnam姓氏());
if(kidsWithToys.hasNext()){
某人加上(“,”);
}
}

对于某些问题,流不是答案,我认为这就是其中之一。

在您的示例中,在完成所有筛选之后,将收集并连接字符串。由于您希望以字符串值结束,因此我们可以创建一个版本的
toString()
(暂时忘记您的流已经包含字符串)来预先计算计数,如下所示:

public static<T> Function<T, String> getCountedToStringFunc(){
    AtomicInteger i = new AtomicInteger(0);
    return t -> i.incrementAndGet() + ". " + t.toString();
}
myStream
.filter(...)
.map(getCountedToStringFunc())
.collect(Collectors.joining(",")));
或者,请注意,如果您希望在将来修改管道,则必须记住在管道中尽可能晚地应用计数器会产生可维护性风险,您可以将其从管道中取出,并使其成为终止功能的一部分:

myStream
.filter(...)
.collect(Collectors.mapping(getCountedToStringFunc(),joining(","))));

后一种方法的优点是,您可以使用它为分区或分组数据创建单独的计数器。

使用第三方免费库(由我编写),它增强了标准流API,您可以使用数字创建流:

String result = StreamEx.of("abc", "def", "aaa", "foo", "a12", "a34", "bar", "aaa")
    .filter(s -> s.startsWith("a"))
    .zipWith(IntStreamEx.ints().boxed(), (str, num) -> (num+1)+". "+str)
    .joining(", ");
// 1. abc, 2. aaa, 3. a12, 4. a34, 5. aaa

基本上:流不打算这样做,如果你有一个
过滤器
,你几乎完全不走运。不,说真的:溪流不能这样做。确实很难看;)我在想有一个合适的方法来做到这一点。如果没有人能很快给出更好的答案,我会接受你的答案。@Tomek正如上面所说,溪流不应该这样。但总有一个(有时是丑陋的)把戏。好主意。通过使用
AtomicInteger
(或其他一些可变计数器)而不是列表,您可以对其进行更大的改进。使用
incrementAndGet
方法,您甚至不需要使用额外的
peek
。我看到的唯一解决方案是在不使用此类破解的情况下,分两次完成。第一个过程让孩子们有玩具,第二个过程在过滤数组的索引上使用
IntStream
,这很好!一分钟前我甚至想到了第二种解决方案。
“为什么不首先使用真正的迭代器对孩子们进行迭代?”
因为我想利用流的惰性和过滤方法……你说你想利用惰性。我认为这意味着您在实际应用程序中没有使用
连接(“,”
收集器,那么?我仍然会使用迭代器。您可以使用Guava构造过滤迭代器,然后进一步修饰它以发出编号的项。这样,过滤仍然是懒惰的。如果不清楚我的意思,我可以编辑我的答案。
myStream
.filter(...)
.map(getCountedToStringFunc())
.collect(Collectors.joining(",")));
myStream
.filter(...)
.collect(Collectors.mapping(getCountedToStringFunc(),joining(","))));
String result = StreamEx.of("abc", "def", "aaa", "foo", "a12", "a34", "bar", "aaa")
    .filter(s -> s.startsWith("a"))
    .zipWith(IntStreamEx.ints().boxed(), (str, num) -> (num+1)+". "+str)
    .joining(", ");
// 1. abc, 2. aaa, 3. a12, 4. a34, 5. aaa