This question already has answers here:
How can I turn a List of Lists into a List in Java 8?
(12 answers)
Closed 6 years ago.
The following example may be trivial, but I have created it to show what I need to achieve using different data (not integers). This code is runnable etc.
List<List<Integer>> master = new ArrayList<>();
for (int i = 0; i < 10; i++) {
List<Integer> inner = new ArrayList<Integer>();
master.add(inner);
for (int j = 0; j < 10; j++) {
inner.add(i * 10 + j);
}
}
System.out.println(master);
//lets make single collections from that but add 1000 to every element - this represents some intermediate operations to generate part of final result
List<Integer> finalAccumulated = new ArrayList<Integer>(); // this will represent our accumulated, final result
for (List<Integer> topLvl : master) {
ArrayList<Integer> intermedialeAccumulated = new ArrayList<>(); //this step is important as it represents returning a collection of results for stream#map not single result
for (Integer nested : topLvl) { // this represents stream#map (or maybe collector?)
intermedialeAccumulated.add(nested + 1000);
}
finalAccumulated.addAll(intermedialeAccumulated); // this represent accumulation of collection of results, not just single result like stream#map do
}
System.out.println(finalAccumulated);
How to get the same result as in finalAccumulated using single Stream. By single I mean that in call chain there can be only single terminate action so the resulting form would be
finalAccumulated=master.stream()...intermediateCalls()...terminateCollectingCall();
Online ide with running code here
Just use a flat map:
List<Integer> finalAccumulated = master
.stream()
.flatMap((x) -> x.stream())
.map((i) -> i + 1000)
.collect(Collectors.toList());
private static void test() {
List<List<Integer>> master = new ArrayList<>();
for (int i = 0; i < 10; i++) {
List<Integer> inner = new ArrayList<Integer>();
master.add(inner);
for (int j = 0; j < 10; j++) {
inner.add(i * 10 + j);
}
}
System.out.println(master);
//lets make single collections from that but add 1000 to every element - this represents some intermediate operations to generate part of final result
List<Integer> finalAccumulated = new ArrayList<Integer>(); // this will represent our accumulated, final result
for (List<Integer> topLvl : master) {
ArrayList<Integer> intermedialeAccumulated = new ArrayList<>(); //this step is important as it represents returning a collection of results for stream#map not single result
for (Integer nested : topLvl) { // this represents stream#map (or maybe collector?)
intermedialeAccumulated.add(nested + 1000);
}
finalAccumulated.addAll(intermedialeAccumulated); // this represent accumulation of collection of results, not just single result like stream#map do
}
//map then using flatmap
List<Integer> finalAccumulated2 = master.stream().map(topLvl -> {
ArrayList<Integer> intermedialeAccumulated = new ArrayList<>();
for (Integer nested : topLvl) {
intermedialeAccumulated.add(nested + 1000);
}
return intermedialeAccumulated;
}).flatMap(intermedialeAccumulated -> intermedialeAccumulated.stream())
.collect(Collectors.toList());
}
Here's how to produce the same finalAccumulated using a single Stream
List<Integer> finalAccumulated = IntStream.range(1000, 1000 + 10 * 10).boxed().collect(Collectors.toList());
Here would be an over-engineered and generic approach for such an operation
private static <T> List<T> flattenWith(List<List<T>> master, Function<T, T> transform) {
return master.stream()
.flatMap(List::stream)
.map(transform)
.collect(Collectors.toList());
}
Usage
flattenWith(master, x -> x + 1000)
Related
Assume I have List<List<Integer>> lstOfintLst , where for each List<Integer> intList I have to check multiple conditions and for each condition I have a set of actions ex count of each condition match , adding them in new List<Integer> matchedLstCon_x etc.
New to Java 8 , I am not clear how can I achieve it with just streaming once instead had to steam for each conditions as below
AtomicInteger count_Unmatched= new AtomicInteger();
AtomicInteger count_Con_1= new AtomicInteger();
AtomicInteger count_Con_2= new AtomicInteger();
AtomicInteger count_Con_3= new AtomicInteger();
List<Integer> list_Unmatched = new ArrayList<Integer>();
List<Integer> list_Con_1 = new ArrayList<Integer>();
List<Integer> list_Con_2 = new ArrayList<Integer>();
List<Integer> list_Con_3 = new ArrayList<Integer>();
lstOfintLst.stream().forEach(intList -> {
boolean isUnmatched=true;
if(intList.stream.allMatch(Condition_1)){
count_Con_1.getAndIncrement();
isUnmatched = false;
list_Con_1.add(intList);
}
if(intList.stream.allMatch(Condition_2)){
count_Con_2.getAndIncrement();
isUnmatched = false;
list_Con_2.add(intList);
}
if(intList.stream.allMatch(Condition_3)){
count_Con_3.getAndIncrement();
isUnmatched = false;
list_Con_3.add(intList);
}
if(isUnmatched){
count_Unmatched.getAndIncrement();
list_Unmatched.add(intList);
}
}
// process all counts & lists
So here we are streaming 3 times for each condition ,on top of this if for a condition if I have a specific action on the elements of the intList I will again have to do a secondary intList.stream. inside the condition
Can we do all condition with one stream ? This can be done with one normal for-loop on intList , irrespective of the number of conditions with may be more variables etc.
If this is the least number of streams then how is it still better for-loop ?
How do I decide which way to go?
Edit : old-fashioned way code added with some ex conditions as asked by #cyberbrain
int count_Unmatched = 0;
int count_Con_1 = 0;
int count_Con_2 = 0;
int count_Con_3 = 0;
List<List<Integer>> list_Unmatched = new ArrayList<>();
List<List<Integer>> list_Con_1 = new ArrayList<>();
List<List<Integer>> list_Con_2 = new ArrayList<>();
List<List<Integer>> list_Con_3 = new ArrayList<>();
for (List<Integer> intList : lstOfintLst) {
boolean isUnmatched = true;
int sum = 0;
int countEven = 0;
int countOdd = 0;
int countMultipleOf3 = 0;
int numLstSize = intList.size();
for (Integer i : intList) {
//all are odd or even
if (i % 2 == 0) {
countEven++;
}
if (i % 2 == 1) {
countOdd++;
}
//all multiples of 3
if (i % 3 == 0) {
countMultipleOf3++;
}
// sum of all elements in intList is multiple of 5
sum = sum + i;
}
if (numLstSize == countEven || numLstSize == countOdd) {
count_Con_1++;
list_Con_1.add(intList);
isUnmatched = false;
}
if (numLstSize == countMultipleOf3) {
count_Con_2++;
list_Con_2.add(intList);
isUnmatched = false;
}
if (sum % 5 == 0) {
count_Con_3++;
list_Con_3.add(intList);
isUnmatched = false;
}
if (isUnmatched) {
count_Unmatched++;
list_Unmatched.add(intList);
}
}
// process all counts & lists
Well, the code you've provided looks like a bit chaotic self-imposed coding challenge or a part of a project which became unmanageable.
Always think about the use cases and data structures that would be suitable for these use cases. Don't cram everything into a single method, instead try to think how can you spit the functionalities - learn about the Single responsibility principle.
You've created four lists of lists, what's next? How are you going to work with them, how can you pass around this data - by wrapping with another list? List of lists of lists, doesn't sound nicely.
The key point is that you need to structure your data in a meaningful way.
Here's one of the possible solutions (disclaimer: I can't say for sure that it would be the most suitable approach for your project).
These lists can be store in a Map as values, and keys of the map would represent matching conditions.
We can introduce an enum encapsulating the conditional logic in a form of Predicates. Members of the enum would be used as keys.
public enum MyPredicate {
IS_EVEN("Is Even", list -> list.stream().allMatch(i -> i % 2 == 0)),
IS_ODD("Is Odd", list -> list.stream().allMatch(i -> i % 2 != 0)),
IS_DIVISIBLE_BY_THREE("Is Divisible By Three", list -> list.stream().allMatch(i -> i % 3 == 0)),
DEFAULT("No Matches", list -> true);
private static final EnumSet<MyPredicate> withoutDefault = EnumSet.range(IS_EVEN, IS_DIVISIBLE_BY_THREE);
private String predicateName;
private Predicate<List<Integer>> predicate;
MyPredicate(String predicateName, Predicate<List<Integer>> predicate) {
this.predicateName = predicateName;
this.predicate = predicate;
}
public static List<MyPredicate> getMatches(List<Integer> list) {
List<MyPredicate> matchingEnums = withoutDefault.stream()
.filter(e -> e.predicate.test(list))
.toList();
return matchingEnums.isEmpty() ? List.of(DEFAULT) : matchingEnums;
}
public boolean matches(List<Integer> list) {
return predicate.test(list);
}
#Override
public String toString() {
return predicateName;
}
}
The process of creating these resulting lists boils down to finding the matching predicate for every list in the input and mapping the list to a matching predicate.
Note: that I've ignored all the counters that you've used in code for a reason. Such logic is absolutely not viable with streams, you should not change the state functions in the stream should not use side effects and accumulate the stated outside the stream. And even with loops, these counters are redundant if you need all elements in a list to match a particular condition.
That's how it might be implemented using Stream.collect():
public static Map<MyPredicate, List<List<Integer>>> splitIntoGroups(List<List<Integer>> lists) {
return lists.stream()
.collect(
HashMap::new,
(Map<MyPredicate, List<List<Integer>>> map, List<Integer> list) ->
MyPredicate.getMatches(list).forEach(p -> {
if (p.matches(list)) map.computeIfAbsent(p, k -> new ArrayList<>()).add(list);
}),
(left, right) -> right.forEach((key, v) ->
left.computeIfAbsent(key, k -> new ArrayList<>()).addAll(v))
);
}
main()
public static void main(String[] args) {
List<List<Integer>> sourceList = List.of(
List.of(1, 2, 3), List.of(4, 6, 8), List.of(5, 7, 9), List.of(3, 9, 18)
);
splitIntoGroups(sourceList).forEach((k, v) -> System.out.println(k + " -> " + v));
}
Output:
Is Divisible By Three -> [[3, 9, 18]]
Is Even -> [[4, 6, 8]]
Is Odd -> [[5, 7, 9]]
No Matches -> [[1, 2, 3]]
So I have a an array List that looks like this: <id,value,id,value,...>and I should find all the elements with the same id and add together there respected values into a result array list (value always comes after the id). At first I thought the problem was easy and went ahead and tried this:
List<String> resultList = new ArrayList<>();
for(int i = 0; i < myList.size(); i+=2){
BigInteger currentID = new BigInteger(myList.get(i));
BigInteger currentSum = new BigInteger(myList.get(i+1));
String currentId = myList.get(i);
int j = 2;
while(j < myList.size()){
if(currentId.equals(myList.get(j))){
BigInteger value2 = new BigInteger(myList.get(j+1));
currentSum = currentID.add(value2);
}
resultList.add(currentId);
resultList.add(String.valueOf(currentSum));
j+=2;
}
}
Needless to say it isn't correct, one of the many problems is that values which haven been already added together will be readded into the result array so I think they should be removed somehow in the loop. Do you guys have any suggestions how to go around this?
Assuming you can use a Map:
My initial thought it to use a Map<String, String> map = new HashMap();
Pseudo code:
For each (id,value pair) in resultList
if map.get(id) exists
add the value to the existing entry in the map
else
add a new entry in the map for (id,value)
As code (NOTE: untested and might not compile, wouldn't copy & paste directly):
Map<String, String> map = new HashMap<>();
for(int i = 0; i < myList.size(); i+=2){
String listId = resultList.get(i); //get the Id from the resultList
String listValue = resultList.get(i+1) //get the value from the resultList
if(map.get(listId) != null) { // if the map has this Id
map.put(listId, map.get(listId)+ listValue); // add the Id's list value
}
else { // if the map doesn't have this Id
map.put(listId, listValue) // add the entry to the map
}
}
You can then take the resulting map and transform is back into a list.
I'll let you write the code to take each entry in the map and add it to a new List. Google can be helpful here.
If you want to use streams you can do it like this.
Given a list like the following:
List<BigInteger> list = List.of(1,10,3,20,3,40,1,5,4,7,1,8,4,9,6,20)
.stream().mapToLong(a -> a).mapToObj(
BigInteger::valueOf).collect(Collectors.toList());
First accumulate the sums in a map with the key being the id.
Map<BigInteger, BigInteger> mapfinal =
IntStream.iterate(0, i -> i < list.size(), i -> i += 2).mapToObj(
i -> new BigInteger[] { list.get(i), list.get(i + 1)
}).collect(Collectors.groupingBy(i -> i[0],
Collectors.reducing(BigInteger.ZERO,
a -> a[1],
(a, b) -> a.add(b))));
You can stop there and just use the map, or you can convert it to a list like the one you started with of alternating id/value pairs.
List<BigInteger> listfinal = mapfinal
.entrySet()
.stream()
.flatMap(e -> Stream.of(e.getKey(), e.getValue()))
.collect(Collectors.toList());
And print the list.
System.out.println(listfinal);
[1, 23, 3, 60, 4, 16, 6, 20]
It is easiest to do this using a Map. But it can also be done without changing the input data structure, or using any intermediate data structure.
Here is an untested code sample. I have simplified the problem by using List<Integer>
List<Integer> myList = ...
List<Integer> resultList = new ArrayList<>();
for (int i = 0; i < myList.size(); i += 2) {
int id = myList.get(i);
// Check that we haven't accumulated this id already
boolean seen = false;
for (int j = 0; j < i && !seen; j += 2) {
seen = myList.get(j) == id;
}
if (seen) {
continue;
}
// Accumulate values for 'id'
int sum = myList.get(i + 1);
for (int j = i + 2; j < myList.size(); j += 2) {
if (myList.get(j) == id) {
sum += myList.get(j + 1);
}
}
// Send to output ...
resultList.add(id);
resultList.add(sum);
}
This solution is O(N^2). And since you are actually using String and BigInteger rather than Integer, that makes it even more expensive.
A solution using a Map should be O(NlogN) or O(N) depending on the map type.
Aside: It would be faster in some cases if you used resultList rather than myList in the first inner loop. But the way I did it above is more obvious ...
I've got the ConcurrentModificationException and do not know why. I know that trying to iterate through a list using for loops and deleting elements inside the loop block is bad idea and can throw such exception, but I have no idea how to fix it in my case.
private static final List<Integer> originalList = new ArrayList<>();
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
originalList.add(i);
}
final int MAX_GROUP_SIZE = 5;
int partitionSize = 4;
List<List<Integer>> partitions = new LinkedList<>();
for (int i = 0; i < originalList.size(); i += partitionSize) {
partitions.add(originalList.subList(i,
Math.min(i + partitionSize, originalList.size())));
}
int lastGroupSize = partitions.get(partitions.size() - 1).size();
if (lastGroupSize < partitionSize && partitions.size() > lastGroupSize){
List<Integer> lastGroup = partitions.remove(partitions.size() - 1);
for (int i = 0; i < lastGroupSize; i++) {
partitions.get(i).add(lastGroup.get(i));
}
}
System.out.println("GROUPS: " + partitions.size());
printGroups(new LinkedList<>(partitions));
}
The problem is that your calls to subList() don't create new lists. As the javadoc says it:
Returns a view of the portion of this list between the specified fromIndex, inclusive, and toIndex, exclusive.
The javadoc also says:
The semantics of the list returned by this method become undefined if the backing list (i.e., this list) is structurally modified in any way other than via the returned list.
When you call partitions.get(i).add(...), you're structurally modifying originalList, causing the error.
I don't believe you intended that, so to fix the problem, you just need to make sure your sub-lists are independent of the original list, i.e. copies, which is easy to do:
new ArrayList<>(originalList.subList(...))
Using the ArrayList(Collection) constructor will create a copy of the sub-list.
So, change this statement:
partitions.add(originalList.subList(i,
Math.min(i + partitionSize, originalList.size())));
to this:
partitions.add(new ArrayList<>(originalList.subList(i,
Math.min(i + partitionSize, originalList.size()))));
You should never iterate the list and perform updating actions while doing so (where updating means adding or removing elements). This is a recipe for disaster.
In order to resolve this there are three possible scenarios to follow:
1) Copy the list, iterate over the copy and remove from the original one.
for (var number : new ArrayList<>(original)) {
if (element > 10) {
original.remove(element);
}
}
2) Use Streams
List<Integer> filtered = original.stream()
.filter(i -> i > 10)
.collect(Collectors.toList());
3) Use an iterator to loop over the list
Iterator<Integer> iterator = original.iterator();
while (iterator.hasNex()) {
Integer number = iterator.next();
if (number > 10) {
iterator.remove();
}
}
Personally I prefer streams.
I have a list of lists:
List<List<String>> someList = new List<List<>>();
The maximum size of a list is five strings. It's something like below:
someList.get(0).size(); // 4 elements
someList.get(1).size(); // 1 elements
someList.get(2).size(); // 3 elements
someList.get(3).size(); // 1 elements
...
I'm trying to devise a method to create a new list of a specific size (1-5 elements) by combining some of the above nested lists. I could do something like the below (in this example, three elements):
public List<String> getThree() {
for (int j = 0; j < someList.size(); j++) {
//look for nested lists of size 3
if (someList.get(j).size() == 3) {
return someList.get(j);
}
for (int j = 0; j < someList.size(); j++) {
//if found nested list of size 2, find one of size 1 to combine
if (someList.get(j).size() == 2) {
for (int k = 0; k < someList.size(); k++) {
if (someList.get(k).size() == 1) {
return someList.get(j).add(someList.get(k).get(0));
}
}
}
for (int j = 0; j < someList.size(); j++) {
//if found nested list of size 1, find one of size 2 to combine
if (someList.get(j).size() == 1) {
for (int l = 0; l < someList.size(); l++) {
if (someList.get(l).size() == 2) {
return someList.get(j).addAll(someList.get(l));
}
}
}
}
}
I haven't included the loop for if no sublists are of size 2, to find three of size 1, but you can imagine how long and how ugly it can get. The order is important, thus the for loops incrementing sequentially (ie. I'd rather combine subList 1 + 2 more than 2 + 3, 1 + 3 more than 2 + 3, etc).
I'm hoping to find a way to dynamically implement this. I can only fathom how unreadable and long the getFive method will be provided my current methodology. I have multiple methods (getOne through getFive), it doesn't need to be dynamic in this sense, I'd just like to get rid of a lot of the if/else and for loops to reduce complexity and improve readability.
I should mention this is homework related, so I don't quite want a specific answer, but a nudge in the right direction. Something modulo perhaps? To do with remainders?
edit; to clarify and give an example:
aList = new List<String>;
aList.add("a");
aList.add("b");
someList.add(aList);
bList = new List<String>;
bList.add("c");
someList.add(bList);
newList = someList.getThree();
//newList.size() == 3
//newList contains "a","b","c"
The getThree() method is creating a new list comprised of elements from the sublists of someList. It cannot split a sublist (ie. it can't take 1 element from a sublist of 2 elements), it's combining whole sublists.
If your intention is to keep collecting from successive lists until you get 5 elements, keep adding then break out when your list is full:
public static List<String> fill(List<List<String>> sources, int size) {
List<String> list = new ArrayList<>();
for (List<String> source : sources)
if (source.size() <= size - list.size())
list.addAll(source);
return list;
}
If you want to consume the largest lists first, add this line as the first line of the method:
Collections.sort(sources, (a, b) -> b.size() - a.size());
In java 8, quite succinct:
public static List<String> fill(List<List<String>> sources, int size) {
return sources.stream().reduce(new ArrayList<>(),
(a, b) -> {if (b.size() <= a.size() - size) a.addAll(b); return a;});
}
and with the largest-first mod:
public static List<String> fill(List<List<String>> sources, int size) {
return sources.stream()
.sorted((a,b) -> b.size() - a.size())
.reduce(new ArrayList<>(), (a, b) ->
{if (b.size() <= a.size() - size) a.addAll(b); return a;});
}
Since you state that the priority of combining Lists is from left to right. An O(N^2) loop is sufficient to handle combining sublists to be less than or equal to your desired amount.
public static void main(String[] args) throws Exception {
List<List<String>> someList = new ArrayList() {{
add(new ArrayList() {{
add("a1");
add("a2");
}});
add(new ArrayList() {{
add("b1");
}});
add(new ArrayList() {{
add("c1");
add("c2");
add("c3");
}});
add(new ArrayList() {{
add("d1");
}});
}};
combine(someList, 4);
for(List<String> subList : someList) {
System.out.println(subList);
}
}
private static void combine(List<List<String>> someList, int combineAmount) {
for (int i = 0; i < someList.size(); i++) {
// Check if the current list already equals or exceeds the combineAmount
if (someList.get(i).size() >= combineAmount) {
continue;
}
// Add sublists to the current sublists until the size of the current
// sublist equals or exceeds the combineAmount
for (int j = i + 1; j < someList.size(); j++) {
if (someList.get(i).size() + someList.get(j).size() > combineAmount) {
continue;
}
someList.get(i).addAll(someList.get(j));
someList.remove(j);
j--;
// Don't bother checking other sublists if the newly
// combined sublists equals or exceeds the combineAmount
if (someList.get(i).size() >= combineAmount) {
break;
}
}
}
}
Results (combineAmount = 4):
[a1, a2, b1, d1]
[c1, c2, c3]
Results (combineAmount = 2):
[a1, a2]
[b1, d1]
[c1, c2, c3]
Results (combineAmount = 6):
[a1, a2, b1, c1, c2, c3]
[d1]
From what I understand you want to combine a list of lists into a total of 5 indexes. When doing this you want it to prioritize the left side first.
Here is a method I have created to do this. I know you did not want a specific example, but I think an example will help you understand as well as help others who also have this question:
private static List<String> getListOf(List<List<String>> someList, int size) {
List<List<String>> combine = new ArrayList<List<String>>();
List<List<String>> combinePrev = new ArrayList<List<String>>();
int value = 0;
int indexCloseValue = 0;
int indexClose;
for(int i = 0; i < someList.size(); i++){//Loops through the lists
value = someList.get(i).size();
boolean[] indexAdded = new boolean[someList.size()];//Used to make sure to not add duplicates
indexAdded[i] = true;
combine.add(someList.get(i));//add current loop to the combine list.
do{//A loop to try to add values other than the one of index i to equal size. This loops multiple times because it may take more than two to equal size.
indexCloseValue = 0;
indexClose = -1;
for(int j = 0; j < someList.size(); j++){
if(!indexAdded[j]){
int listSize = someList.get(j).size();
if(value + listSize > indexCloseValue && value + listSize <= size){
indexCloseValue = listSize;
indexClose = j;
}
}
}
if(indexClose == -1){
break;
}else{
combine.add(someList.get(indexClose));
value+=indexCloseValue;
indexAdded[indexClose] = true;
}
}while(value + indexCloseValue < size);
int added = 0;
for(List<String> str : combine){//Check size of combine list
added+=str.size();
}
int addedPrev = 0;
for(List<String> str : combinePrev){//Check size of combinePrev list
addedPrev+=str.size();
}
if(added > addedPrev && added <= size){
combinePrev = new ArrayList<List<String>>(combine);//Set combinePrev to combine if it is larger but less than size
}
combine = new ArrayList<List<String>>();//Reset combine
}
List<String> returnList = new ArrayList<String>();
for(List<String> list : combinePrev){//converts double list to a single list of strings at length "size".
for(String str : list){
returnList.add(str);
}
}
return returnList;
}
If there are any problems with this code or you have a question ask me in the comments.
I have a 2D List of type Integer (ArrayList< List < Integer > >),
There are currently 177147 lists of integers.
I want to separate these into 243 different collections (Each collection having 729 lists of elements)
e.g. Array[0] -> Array[728] go into Collection[0]
...until
Array[176417] -> Array[177146] goes into Collection[242]
Should I rather use ArrayList<ArrayList<List<Integer>>>?
If so, how do I separate these items in this manner?
Iterate on the elements and add by slices to the new structure:
public void run() {
int SLICE = 729;
List<List<Integer>> list2d = new ArrayList<List<Integer>>();
// fill original list
List<List<List<Integer>>> list3d = new ArrayList<List<List<Integer>>>();
List<List<Integer>> partial = new ArrayList<List<Integer>>(SLICE);
for (List<Integer> list : list2d) {
partial.add(list);
if (partial.size() == SLICE) {
list3d.add(partial);
partial = new ArrayList<List<Integer>>(SLICE);
}
}
if (!partial.isEmpty()) {
list3d.add(partial);
}
}
for(int c = 0; c <= 242; c++)
{
for(int i = 0; i < 729; i++)
{
int position = (c * 729) + i;
Collection[c][i] = Array[position];
}
}
You might want to check my maths on the formula for position, but the idea is sound.