问题描述
我是Java 8的新手:Streams
和Collectors
类.
I am new to Java 8: Streams
and Collectors
classes.
我正在读取一个文件,其内容需要保存在LinkedHashMap<Integer, String>
中,其中<keys>
是文件的行号,而<values>
是每一行的内容.
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.
在这里,我想使用Stream
概念,但是我不能使用Collectors.toMap
自动增加<keys>
,这需要保存在LinnkedHashMap
对象中.取而代之的是,我得到了例外.
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:
首先,只要您拥有对象的List<E>
(即E
可能是String
,Integers
,Objects
等),就可以使用IntStream
将其转换为Map<Integer, E>
. >类.此类是一系列原始整数值元素,它们支持顺序和并行聚合操作.这意味着就像一个巨大的柜台.如果我们已经有一个计数器,则需要设置一些限制,IntStream.range(int start, int end)
方法将为我们提供帮助.此方法将以递增步长1将顺序从start
(包括)到end
(不包括)的有序IntStream
返回.因此,如果要创建具有List
用途的大小的IntStream
这个:
First, whenever you have a List<E>
of objects (i.e. 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.在IntStream
对象的基础上准备一个Stream
对象.
现在,我们有一个与您的List<E>
大小相同的计数器,但是我们需要一个Map<Integer, E>
.好了,现在我们要使用IntStream.boxed()
.此方法返回一个Stream
,该元素由该流的元素组成,每个元素都装在一个Integer
中.这是Stream<Integer>
.我们快完成了.
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.将Stream
对象转换为Map
对象
最后,我们可以使用Stream.collect()
方法创建地图.此方法对该流的元素执行可变还原操作.如果没有Collectors.toMap()
方法的帮助,这种减少将很复杂.该收集器可用于将Stream元素收集到Map实例中.为此,我们需要提供两个功能:keyMapper
和valueMapper
. keyMapper
将用于从Stream
元素中提取Map
密钥,而valueMapper
将用于提取与给定<key>
关联的<value>
.对于我们的示例,我们将使用Map<Integer, Integer>
. keyMapper
将使用i -> i
提取我们可以提取的steamBoxed
流的值,而valueMapper
应该是使用i -> numbers.get(i)
可以得到的numbers
列表的值,如下所示:
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.结合所有步骤
这三个部分可以通过以下简单代码组合在一起:
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>
此外,您还会发现一些作者更喜欢将Function.identity()
方法用作keyMapper
,而将numbers::get
lambda表达式用作valueMapper
. 他们为什么使用这些表达式? Function.identity()
方法将始终返回相同的实例.因此,使用Function.identity()
代替i -> i
可能会节省一些内存.但是,i -> i
比Function.identity()
更具可读性,但是由于创建自己的实例并具有不同的实现类会消耗更多的内存. ::
lambda表达式只是方法参考捕获.
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.
好吧,像这样:
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>
选择
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>
这篇关于如何在Java 8中使用收集器和流自动增加哈希图的键的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!