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