我有一个Java对象列表,我需要通过数据库选择聚合函数来减少它。
注意:数据是从多个数据库和服务调用计算得出的。我希望有数千行,每行总是会有相同数量的“单元格”。此数量在执行之间发生变化。
示例:
假设我的数据以List
Object[3]
(List<Object[]>
)表示,我的数据可能是:
[{"A", "X", 1},
{"A", "Y", 5},
{"B", "X", 1},
{"B", "X", 2}]
示例1:
SUM over index 2,按索引0和1分组
[{"A", "X", 1},
{"A", "Y", 5},
{"B", "X", 3}]
示例2:
MAX超过索引2,按索引0分组
[{"A", "Y", 5},
{"B", "X", 2}]
有人知道一些可以在Java中模拟这种行为的框架或api吗?
我的第一个选择是在NO-SQL数据库中插入所有数据(如Couchbase),然后应用Map-Reduce,最后得到结果。但是这个解决方案有很大的开销。
我的第二个选择是嵌入一个Groovy脚本,但它也有很大的开销。
答案 0 :(得分:5)
如果Java 8是一个选项,那么您可以使用Stream.collect实现所需。
例如:
import static java.util.stream.Collectors.*;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
public class Example
{
public static void main(String[] args)
{
List<List<Object>> list = Arrays.asList(
Arrays.<Object>asList("A", "X", 1),
Arrays.<Object>asList("A", "Y", 5),
Arrays.<Object>asList("B", "X", 1),
Arrays.<Object>asList("B", "X", 2)
);
Map<Set<Object>, List<List<Object>>> groups = list.stream()
.collect(groupingBy(Example::newGroup));
System.out.println(groups);
Map<Set<Object>, Integer> sums = list.stream()
.collect(groupingBy(Example::newGroup, summingInt(Example::getInt)));
System.out.println(sums);
Map<Set<Object>, Optional<List<Object>>> max = list.stream()
.collect(groupingBy(Example::newGroup, maxBy(Example::compare)));
System.out.println(max);
}
private static Set<Object> newGroup(List<Object> item)
{
return new HashSet<>(Arrays.asList(item.get(0), item.get(1)));
}
private static Integer getInt(List<Object> items)
{
return (Integer)items.get(2);
}
private static int compare(List<Object> items1, List<Object> items2)
{
return (((Integer)items1.get(2)) - ((Integer)items2.get(2)));
}
}
提供以下输出:
{[A, X]=[[A, X, 1]], [B, X]=[[B, X, 1], [B, X, 2]], [A, Y]=[[A, Y, 5]]}
{[A, X]=1, [B, X]=3, [A, Y]=5}
{[A, X]=Optional[[A, X, 1]], [B, X]=Optional[[B, X, 2]], [A, Y]=Optional[[A, Y, 5]]}
或者,使用Java 8示例作为灵感,虽然有点冗长,但您可以在旧版本的Java中实现相同的功能:
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Example
{
public static void main(String[] args)
{
List<List<Object>> list = Arrays.asList(
Arrays.<Object>asList("A", "X", 1),
Arrays.<Object>asList("A", "Y", 5),
Arrays.<Object>asList("B", "X", 1),
Arrays.<Object>asList("B", "X", 2)
);
Function<List<Object>, Set<Object>> groupBy = new Function<List<Object>, Set<Object>>()
{
@Override
public Set<Object> apply(List<Object> item)
{
return new HashSet<>(Arrays.asList(item.get(0), item.get(1)));
}
};
Map<Set<Object>, List<List<Object>>> groups = group(
list,
groupBy
);
System.out.println(groups);
Map<Set<Object>, Integer> sums = sum(
list,
groupBy,
new Function<List<Object>, Integer>()
{
@Override
public Integer apply(List<Object> item)
{
return (Integer)item.get(2);
}
}
);
System.out.println(sums);
Map<Set<Object>, List<Object>> max = max(
list,
groupBy,
new Comparator<List<Object>>()
{
@Override
public int compare(List<Object> items1, List<Object> items2)
{
return (((Integer)items1.get(2)) - ((Integer)items2.get(2)));
}
}
);
System.out.println(max);
}
public static <K, V> Map<K, List<V>> group(Collection<V> items, Function<V, K> groupFunction)
{
Map<K, List<V>> groupedItems = new HashMap<>();
for (V item : items)
{
K key = groupFunction.apply(item);
List<V> itemGroup = groupedItems.get(key);
if (itemGroup == null)
{
itemGroup = new ArrayList<>();
groupedItems.put(key, itemGroup);
}
itemGroup.add(item);
}
return groupedItems;
}
public static <K, V> Map<K, Integer> sum(Collection<V> items, Function<V, K> groupFunction, Function<V, Integer> intGetter)
{
Map<K, Integer> sums = new HashMap<>();
for (V item : items)
{
K key = groupFunction.apply(item);
Integer sum = sums.get(key);
sums.put(key, sum != null ? sum + intGetter.apply(item) : intGetter.apply(item));
}
return sums;
}
public static <K, V> Map<K, V> max(Collection<V> items, Function<V, K> groupFunction, Comparator<V> comparator)
{
Map<K, V> maximums = new HashMap<>();
for (V item : items)
{
K key = groupFunction.apply(item);
V maximum = maximums.get(key);
if (maximum == null || comparator.compare(maximum, item) < 0)
{
maximums.put(key, item);
}
}
return maximums;
}
private static interface Function<T, R>
{
public R apply(T value);
}
}
提供以下输出:
{[A, X]=[[A, X, 1]], [A, Y]=[[A, Y, 5]], [B, X]=[[B, X, 1], [B, X, 2]]}
{[A, X]=1, [A, Y]=5, [B, X]=3}
{[A, X]=[A, X, 1], [A, Y]=[A, Y, 5], [B, X]=[B, X, 2]}
答案 1 :(得分:2)
使用内存中的SQL数据库,如SQL lite,H2,Derby或其他SQL数据库。创建一个与每行元素匹配的表。使用查询不同数据集的结果填充它。然后使用您需要的任何排序和分组选项查询内存表。
我同意使用内存数据库可能有点过分,但代码将更具可读性,并且RDBMS是针对这些类型的查询而做的。
答案 2 :(得分:0)
如果您愿意使用第三方库而不需要并行性,那么jOOλ会在标准JDK Stream
和Collectors
Map<Tuple2<Object, Object>, Optional<Object>> map =
Seq.seq(list)
.groupBy(a -> tuple(a[0], a[1]), Agg.sum(a -> a[2]));
System.out.println(map);
屈服
{(B, X)=Optional[3],
(A, X)=Optional[1],
(A, Y)=Optional[5]}
Map<Object, Optional<Integer>> map =
Seq.seq(list)
.groupBy(a -> a[0], Agg.max(a -> (Integer) a[2]));
System.out.println(map);
屈服
{A=Optional[5], B=Optional[2]}
免责声明:我为jOOλ背后的公司工作