简体   繁体   中英

How to print a list by adding a new line after every 3rd element in a list in java lambda expression?

Suppose I have a list as below

Collection<?> mainList = new ArrayList<String>();
mainList=//some method call//

Currently, I am displaying the elements in the list as

System.out.println(mainList.stream().map(Object::toString).collect(Collectors.joining(",")).toString());

And I got the result as

a,b,c,d,e,f,g,h,i

How to print this list by adding a new line after every 3rd element in a list in java, so that it will print the result as below

a,b,c
d,e,f
g,h,i

Note: This is similar to How to Add newline after every 3rd element in arraylist in java? .But there formatting the file is done while reading itself. I want to do it while printing the output.

If you want to stick to Java Stream API then your problem can be solved by partitioning initial list to sublists of size 3 and then representing each sublist as a String and joining results with \\n .

import java.util.AbstractMap;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

final class PartitionListExample {

    public static void main(String[] args) {

        final Collection<String> mainList = Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i");
        final AtomicInteger idx = new AtomicInteger(0);
        final int size = 3;

        // Partition a list into list of lists size 3
        final Collection<List<String>> rows = mainList.stream()
                .collect(Collectors.groupingBy(
                        it -> idx.getAndIncrement() / size
                ))
                .values();

        // Write each row in the new line as a string
        final String result = rows.stream()
                .map(row -> String.join(",", row))
                .collect(Collectors.joining("\n"));

        System.out.println(result);
    }
}

There are 3rd party libraries that provide utility classes that makes list partitioning easier (eg Guava or Apache Commons Collections ) but this solution is built on Java 8 SDK only.

What it does is:

  • firstly we collect all elements by grouping by assigned row index and we store values as a list (eg {0=[a,b,c],1=[d,e,f],2=[g,h,i]}
  • then we take a list of all values like [[a,b,c],[d,e,f],[g,h,i]]
  • finally we represent list of lists as a String where each row is separated by \\n

Output Demo

Running following program will print to console following output:

a,b,c
d,e,f
g,h,i

Getting more from following example

Alnitak played even more with following example and came up with a shorter solution by utilizing Collectors.joining(",") in .groupingBy collector and using String.join("\\n", rows) in the end instead of triggering another stream reduction.

        final Collection<String> rows = mainList.stream()
                .collect(Collectors.groupingBy(
                        it -> idx.getAndIncrement() / size,
                        Collectors.joining(",")
                ))
                .values();

        // Write each row in the new line as a string
        final String result = String.join("\n", rows);

        System.out.println(result);
    }

Final note

Keep in mind that this is not the most efficient way to print list of elements in your desired format. But partitioning list of any elements gives you flexibility if it comes to creating final result and is pretty easy to read and understand.

A side remark : in your actual code, map(Object::toString) could be removed if you replace

Collection<?> mainList = new ArrayList<String>(); by

Collection<String> mainList = new ArrayList<String>(); .

If you manipulate String s, create a Collection of String rather than Collection of ? .


But there formatting the file is done while reading itself.I want to do it while printing the output.

After gotten the joined String, using replaceAll("(\\\\w*,\\\\w*,\\\\w*,)", "$1" + System.lineSeparator()) should do the job.
Iit will search and replace all series of 3 characters or more separated by a , character by the same thing ($1-> group capturing) but by concatenating it with a line separator.

Besides this :

String collect = mainList.stream().collect(Collectors.joining(","));    

could be simplified by :

String collect = String.join(",", mainList);    

Sample code :

public static void main(String[] args) {
  Collection<String> mainList = Arrays.asList("a","b","c","d","e","f","g","h","i", "j");
  String formattedValues = String.join(",", mainList).replaceAll("(\\w*,\\w*,\\w*,)", "$1" + System.lineSeparator());
  System.out.println(formattedValues);
}

Output :

a,b,c,

d,e,f,

g,h,i,

j

Another approach that hasn't been answered here is to create a custom Collector .

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collectors;

public class PartitionListInPlace {

    static class MyCollector implements Collector<String, List<List<String>>, String> {

        private final List<List<String>> buckets;
        private final int bucketSize;

        public MyCollector(int numberOfBuckets, int bucketSize) {
            this.bucketSize = bucketSize;
            this.buckets = new ArrayList<>(numberOfBuckets);
            for (int i = 0; i < numberOfBuckets; i++) {
                buckets.add(new ArrayList<>(bucketSize));
            }
        }

        @Override
        public Supplier<List<List<String>>> supplier() {
            return () -> this.buckets;
        }

        @Override
        public BiConsumer<List<List<String>>, String> accumulator() {
            return (buckets, element) -> buckets
              .stream()
              .filter(x -> x.size() < bucketSize)
              .findFirst()
              .orElseGet(() -> {
                  ArrayList<String> nextBucket = new ArrayList<>(bucketSize);
                  buckets.add(nextBucket);
                  return nextBucket;
              })
              .add(element);
        }

        @Override
        public BinaryOperator<List<List<String>>> combiner() {
            return (b1, b2) -> {
                throw new UnsupportedOperationException();
            };
        }

        @Override
        public Function<List<List<String>>, String> finisher() {
            return buckets -> buckets.stream()
              .map(x -> x.stream()
                .collect(Collectors.joining(", ")))
              .collect(Collectors.joining(System.lineSeparator()));
        }

        @Override
        public Set<Characteristics> characteristics() {
            return new HashSet<>();
        }
    }


    public static void main(String[] args) {
        Collection<String> mainList = Arrays.asList("a","b","c","d","e","f","g","h","i", "j");

        String formattedValues = mainList
          .stream()
          .collect(new MyCollector(mainList.size() / 3, 3));
        System.out.println(formattedValues);
    }
}

Explanation

This is a mutable collector that should not be used in parallel. If your necessities require that you process the stream in parallel you will have to transform this collector to be thread safe, which is pretty easy if you don't care about the order of the elements.

The combiner throws an exception because it is never called since run the stream sequentially.

The set of Characteristics has none that interests us, you can verify this by reading the javadoc

The supplier method will fetch the bucket in which we want to insert the element. The element will be insert in the next bucket that has space, otherwise we will create a new bucket and add it there.

The finisher is quite simple: Join the contents of each bucket by , and join the buckets themselves with System.lineSeparator()

Remember

Do not use this collector to process

Output

a, b, c
d, e, f
g, h, i
j

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM