I managed, but it was a lot of work:
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author Victor
*/
public class StreamArray {
// Obtido daqui: https://stackoverflow.com/a/15497288/540552
public static Object[][] transposeMatrix(Object[][] m) {
Object[][] temp = new Object[m[0].length][m.length];
for (int i = 0; i < m.length; i++) {
for (int j = 0; j < m[0].length; j++) {
temp[j][i] = m[i][j];
}
}
return temp;
}
// Converte o List<List<Object>> em Object[][], faz a transposição, e converte de volta em List<List<Object>>.
public static List<List<Object>> transpose(List<List<Object>> m) {
Object[][] a = m.stream().map(List::toArray).collect(Collectors.toList()).toArray(new Object[][] {});
Object[][] b = transposeMatrix(a);
return Arrays.asList(b).stream().map(Arrays::asList).collect(Collectors.toList());
}
public static List<Object> join(List<List<Object>> lists) {
// Recebemos um List<List<Object>> aonde o List interno é um conjunto de chaves e valores.
// O List externo é uma lista de conjuntos de chaves e valores.
return transpose(lists)
.stream()
// Agora temos um Stream<List<Object>> diferente.
// O List interno é um conjunto de elementos em uma mesma posição.
// O Stream externo é uma lista de conjuntos de elementos em uma mesma posição.
// Em cada List interno, soma todos os valores. O resultado é um Stream<Object>, aonde cada Object é
// a soma dos valores em uma dada posição.
.map(x -> x.stream().collect(Collectors.summingDouble(v -> ((BigDecimal) v).doubleValue())))
.collect(Collectors.toList());
}
private static Stream<List<Object>> calcularTotal2(Stream<List<Object>> st, int chave) {
return st
.collect(Collectors.groupingBy(o -> o.get(chave), Collectors.toList())) // Agora temos um Map<Object, List<List<Object>>>
.entrySet() // Temos agora um Collection<Map.Entry<Object, List<List<Object>>>>
.stream()
// Temos agora um Stream<Map.Entry<Object, List<List<Object>>>>.
// A lista interna equivale a um array contendo a chave e os valores.
// A lista intermediária é um conjunto de listas representando chaves e valores tal que todas tem a mesma chave.
// O entry é a relação de chaves para listas intermediárias.
// O stream externo é o conjunto total.
.map(e -> {
List<Object> in = StreamArray.join(e.getValue()); // Junta as listas intermediárias.
in.set(chave, e.getKey()); // Coloca a chave de volta.
return in;
});
}
private static List<Object[]> calcularTotal(List<Object[]> lista, int chave) {
return calcularTotal2(lista.stream().map(Arrays::asList), chave)
.map(x -> x.toArray(new Object[x.size()]))
.collect(Collectors.toList());
}
public static void main(String... args) {
// Cria alguns valores BigDecimal para colocar no List<Object[]>
BigDecimal BD_30 = BigDecimal.valueOf(30);
BigDecimal BD_45 = BigDecimal.valueOf(45);
BigDecimal BD_50 = BigDecimal.valueOf(50);
BigDecimal BD_60 = BigDecimal.valueOf(60);
BigDecimal BD_100 = BigDecimal.valueOf(100);
BigDecimal BD_200 = BigDecimal.valueOf(200);
BigDecimal BD_400 = BigDecimal.valueOf(400);
// Vamos calcular com isso. A posição 0 de cada array é a chave.
List<Object[]> a = Arrays.asList(
new Object[] {BD_45, BD_100, BD_200},
new Object[] {BD_45, BD_200, BD_400},
new Object[] {BD_50, BD_30, BD_60});
// Faz a mágica.
List<List<Object>> b = calcularTotal(a, 0) // Faz a mágica.
.stream()
.map(Arrays::asList) // Transforma os arrays internos em listas, assim o System.out imprime eles de uma forma legal.
.collect(Collectors.toList());
System.out.println(b);
}
}
Here's the output:
[[45, 300.0, 600.0], [50, 30.0, 60.0]]
First, I used the code to transpose matrices from here: link
The reason you need to do this is that when we have a list of lists, to get a list of sums, just apply to each element of the external list something that adds all the elements of a list. This is much easier than creating a list where each element is a position that contains the sum of the elements of the inner list in that same position.
Having the code to transpose an array, to apply it to a list of lists, I first need to convert it to array, transpose, and then convert back into list of lists.
In the code, you should realize that I made the calcularTotal2
method work with Stream<List<Object>>
instead of Stream<Object[]>
or List<Object[]>
. The reason for this is that mixing arrays with streams is awful and the code would be much more complicated if I did not do that. The calcularTotal
method only converts List<Object[]>
to Stream<List<Object>>
and converts Stream<List<Object>>
to List<Object[]>
Considerations about this:
-
It is certainly possible to transpose the list of lists without having to convert to array and convert back later, but this is somewhat laborious because the array is already created with all the necessary positions, whereas in the list of lists , both the external list and the inner list are created empty, and you can not simply "setate" an element in a position without this position exists before.
-
In the end I'm converting from array to list in calcularTotal
by converting back to array in transpose
, converting back to list in transpose
and converting back to array in calcularTotal
. Obviously I could not do all these conversions and work with arrays from start to finish, but again I repeat that arrays and streams do not match.
-
Finally, using arrays directly this way is usually a sign that there is a problem with object orientation, especially if the array is of type Object
. What the array represents maybe should be a specific class with specific methods, especially considering that one of its positions has a special meaning (the key). In this case, it is possible for the algorithm / program to come out in a much more elegant way using more object orientation and less lists, maps, and arrays.
-
% s of% s, like any other java tool are not silver bullets and can be misused and misused just like anything else. That is, sometimes (but not always, obviously) it is easier and more practical to use the old Stream
of always and to leave for
s aside. You can also use a hybrid strategy where there is a Stream
on the outside and a Stream
on the inside or vice versa, or something like that.