简体   繁体   中英

How to auto increment the key of a hashmap using collectors and stream in java 8

I am new to Java 8: Streams and Collectors classes.

I am reading a file whose content need to be saved in a LinkedHashMap<Integer, String> where its <keys> are the line numbers of the files and its <values> are the content at each line, respectably.

Here, I want to use the Stream concept but I am not able to use Collectors.toMap to auto-increment the <keys> , which need to be saved in the LinnkedHashMap object. Instead of that I am getting exceptions.

Following is the code which i am trying:

List<String> list = new ArrayList<>();
Integer count = 0;

try (BufferedReader br = Files.newBufferedReader( Paths.get( fileName ) )) {
    // br returns as stream and convert it into a List
    list = br.lines().collect( Collectors.toList() );
}
catch ( IOException e ) {
    e.printStackTrace();
}

list.forEach( System.out::println );

Map<Integer, String> fileNumWithContentMapper = list.stream()
        .collect( Collectors.toMap( n->n+1,s1->s1));

There are many ways to to this. But here I will explain my way:

1. Set the size of a IntStream object.

First, whenever you have a List<E> of objects (ie E could be String , Integers , Objects , etc.) you can convert it into a Map<Integer, E> by using the IntStream class. This class is a sequence of primitive int-valued elements that supports sequential and parallel aggregate operations. This means is like a huge counter. If we already have a counter we need to put some limits and the IntStream.range(int start, int end) method will help us. This method will returns a sequential ordered IntStream from start (inclusive) to end (exclusive) by an incremental step of 1. So if you want to create a IntStream with the size of our List use this:

List<Integer> numbers = Arrays.asList(4, 5, 4, 3);
IntStream stream = IntStream.range(0, numbers.size); 

2. Prepare a Stream object based on the IntStream object.

Now, we have a counter of the size of your List<E> , but we need a Map<Integer, E> . Well, now we gonna use the IntStream.boxed() . This method returns a Stream consisting of the elements of this stream, each boxed to an Integer . This is a Stream<Integer> . We are almost done.

Stream<Integer> streamBoxed = stream.boxed();

3. Convert the Stream object to a Map object

Finally, we can create the map, using the Stream.collect() method. This method performs a mutable reduction operation on the elements of this stream. This reduction will be complicated if we didn't have the help of Collectors.toMap() method. This collector can be used to collect Stream elements into a Map instance. To do this, we need to provide two functions: keyMapper and valueMapper . The keyMapper will be used for extracting a Map key from a Stream element, and valueMapper will be used for extracting a <value> associated with a given <key> . For our example, we will use a Map<Integer, Integer> . The keyMapper will the values of the steamBoxed stream we can extract, using i -> i , and the valueMapper should be the values of the numbers list we will get those, using i -> numbers.get(i) like this:

Map<Integer, Integer> result = streamBoxed.collect(Collectors.toMap(i -> i, i -> numbers.get(i)))

4. Combine all the steps

These three parts can be combined together in this simple code:

List<Integer> numbers = Arrays.asList(4, 5, 4, 3);

Map<Integer, Integer> result = IntStream
        .range(0, numbers.size); // IntStream 
        .boxed(); // Stream<Integer>
        .collect(Collectors.toMap(i -> i, i -> numbers.get(i))) // Map<Integer, Integer>

Also, you will find that some authors prefer to use Function.identity() method as a keyMapper , and numbers::get lambda expression as valueMapper . Why they use those expressions? Just for preference. The Function.identity() method will always return the same instance. So, using Function.identity() instead of i -> i might save some memory. However, i -> i is more readable than Function.identity() but because creates its own instance and have a distinct implementation class consume more memory. The :: lambda expression is just a method reference capture.

But, how can I apply this to my solution?

Well, like this:

final List<String> list;

...
// list initialization;
list = br.lines().collect(Collectors.toList());
...

Map<Integer, String> fileNumWithContentMapper = IntStream
        .range(0, list.size()) // IntStream 
        .boxed() // Stream<Integer>
        .collect(Collectors.toMap(i -> i, i -> list.get(i))); // Map<Integer, String>
Alternative
final List<String> list; ... // list initialization; list = br.lines().collect(Collectors.toList()); ... Map<Integer, String> fileNumWithContentMapper = IntStream .range(0, list.size()) // IntStream .boxed() // Stream<Integer> .collect(Collectors.toMap(Function.identity(), list::get)) // Map<Integer, String>

you can use IntStream.range :

IntStream.range(0, list.size())
         .boxed()
         .collect(Collectors.toMap(Function.identity(), i -> list.get(i)));

Another option would be using the LineNumberReader API.

try this code:

public static void main(String[] args)  {
    List<String> list = Arrays.asList("A", "B", "C");
    list.forEach( System.out::println );

    AtomicInteger i = new AtomicInteger(0);
    Map<Integer, String> fileNumWithContentMapper = list.stream()
                .collect( Collectors.toMap( n->i.incrementAndGet(),s1->s1));

    System.out.println(fileNumWithContentMapper);
}

Suppose you have List like below:

List<String> list =  Arrays.asList("Vishwa","Ram","Mohan","Sohan");

Now you want Output Like below :

0  Vishwa
1  Ram
2  Mohan
3  Sohan

public class Someclass{
static int j=0;
static int count(int de){
    return de++;
}
    public static void main(String[] args) {
     List<String> list =  Arrays.asList("Vishwa","Ram","Mohan","Sohan");
        Map<Integer,String> map;
        map = list.stream().collect(Collectors.toMap(s->{count(j);return j++;}, Function.identity()));
        map.forEach((k,v)-> {
                         System.out.print(k+"  ");
                         System.out.println(v);
    });
    }
}
Stream.of("A", "B", "C").collect(TreeMap<Integer, String>::new
    ,(map, element) -> map.put(Optional.ofNullable(map.lastEntry()).map(e -> e.getKey() + 1).orElse(1), element)
    ,(m1, m2) -> {})
    .forEach((i, e) -> System.out.println(i + " " + e));

Prints:

1 A 
2 B
3 C

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