Reputation: 1457
I have this list:
List<String> names= Arrays.asList(new String[]{"Tom", "louis", "martha", "Tom", "john", "Carol", "Tom", "Simon"});
I want to create three different lists where I put the values between Tom word:
"Tom", "louis", "martha";
"Tom", "john", "Carol";
"Tom", "Simon"
I was doing with indexes in a traditional way
My way was just;
List<Integer> positionsWithTom = new ArrayList<>();
for(int i=0; i<names.size; i++){
...
positionsWithTom = [0,3,6]
}
and then put the values between in different lists
There is not a much elegant by doing this, maybe with stream filtering somehow?
Upvotes: 1
Views: 227
Reputation: 40034
I don't believe this easily lends itself to streams. Nor would a stream approach be any more clear in expressing the problem statement than a simple loop.
As long as you don't care what order the sublists are in, it is pretty straight forward using List.lastIndexOf
and a while loop
. Note that since this chews up the original list and the fact that these sublists don't overlap, there is no chance of the sublists being altered by changes to the original list or by each other (unless a reference to the original exists). But one can always pass the sublist
as an argument to the ArrayList
constructor if this is a concern.
List<String> names = new ArrayList<>(List.of("Tom", "louis",
"martha", "Tom", "john", "Carol", "Tom", "Simon"));
List<List<String>> lists = new ArrayList<>();
int start;
while ((start = names.lastIndexOf("Tom")) >= 0) {
ists.add(names.subList(start, names.size()));
names = names.subList(0, start);
}
lists.forEach(System.out::println);
Prints
[Tom, Simon]
[Tom, john, Carol]
[Tom, louis, martha]
Note that the order can be reversed by specifying an index of 0
in the add
method. But that can be expensive for ArrayLists
due to the copying. A LinkedList
would alleviate that expense for a bit of overhead.
And just for completion, here is my stream solution.
sublists
based on where Tom
is located. So the first list contains all the names, the second, all but the last "Tom"
grouping, and so forth.Tom
from the end.List<List<String>> result = Stream.iterate(
names,
n -> n.size() > 0 && n.lastIndexOf("Tom") >= 0,
n -> n.subList(0, n.lastIndexOf("Tom")))
.map(n -> n.subList(n.lastIndexOf("Tom"), n.size()))
.collect(Collectors.toList());
result.forEach(System.out::println);
Prints
[Tom, Simon]
[Tom, john, Carol]
[Tom, louis, martha]
I still prefer the while loop solution as I think it is cleaner and more succinct. Someone more proficient in streams could probably do it better.
Upvotes: 1
Reputation: 3433
First I collected the indexes of elements ([0, 3, 6]
) that have the value "Tom"
to the tomindex
list. Then I used subList() method.
This part:
j == tomindex.size() - 1 ? names.subList(tomindex.get(j), names.size())
: names.subList(tomindex.get(j), tomindex.get(j + 1))
is not to miss the last one. Last element of tomindex
is 6
and we need to fetch values between the indexes 6
and 8
so we use names.size()
at the end.
Try this:
List<Integer> tomindex = IntStream.range(0, names.size()).filter(i -> names.get(i).equals("Tom")).boxed()
.collect(Collectors.toList());
Collection<List<String>> subLists = IntStream.range(0, tomindex.size())
.mapToObj(j -> j == tomindex.size() - 1 ? names.subList(tomindex.get(j), names.size())
: names.subList(tomindex.get(j), tomindex.get(j + 1)))
.collect(Collectors.toList());
System.out.println(subLists);
Output:
[[Tom, louis, martha], [Tom, john, Carol], [Tom, Simon]]
Upvotes: 1