如果我有这样的数组列表(伪java代码(:
请注意,valsSorted列表将始终按x[0]asc和x[1]desc顺序排序。
List valsSorted = {[1 5][1 4][1 3][2 1][3 2][3 1][4 2][4 1][5 1][6 2][6 1]};
我如何用Java8流和Lambda过滤这个列表,以便获得:
result = {[1 5][2 1][3 2][4 2][5 1][6 2]}
数组的第一项(x[0](是ID,第二项是版本号。因此,规则是返回具有最高版本的所有不同ID。
如果我使用for循环,下面的代码就可以了:
ArrayList<int[]> result= new ArrayList();
int keep = -1;
for (int[] x : valsSorted) {
int id = x[0];
int version = x[1];
if(keep == id) continue;
keep = id;
result.add(x);
}
使用单词"distinct"建议使用distinct()
流操作。不幸的是,该操作被硬连接为使用流元素的equals()
方法,这对数组没有用处。处理此问题的一种方法是将数组包装在一个包装器对象中,该包装器对象具有您正在寻找的相等语义:
class Wrapper {
final int[] array;
Wrapper(int[] array) { this.array = array; }
int[] getArray() { return array; }
@Override
public boolean equals(Object other) {
if (! (other instanceof Wrapper))
return false;
else
return this.array[0] == ((Wrapper)other).array[0];
}
@Override
public int hashCode() { ... }
}
然后在distinct()
之前包装您的对象,并在之后打开它
List<int[]> valsDistinct =
valsSorted.stream()
.map(Wrapper::new)
.distinct()
.map(Wrapper::getArray)
.collect(toList());
这会对数据进行一次传递,但它会为每个值生成一个垃圾对象。这也依赖于按顺序处理的流元素,因为您想要第一个。
另一种方法是使用某种有状态收集器,但这最终会在任何后续处理开始之前存储整个结果列表,而您曾说过要避免这种情况。
可能值得考虑将数据元素设置为实际的类,而不是两个元素数组。通过这种方式,您可以提供一个合理的平等概念,还可以使值具有可比性,以便您可以轻松地对它们进行排序。
(来源:从这个答案中窃取的技术。(
class Test{
List<Point> valsSorted = Arrays.asList(new Point(1,5),
new Point(1,4),
new Point(1,3),
new Point(2,1),
new Point(3,2),
new Point(3,1),
new Point(4,2),
new Point(4,1),
new Point(5,1),
new Point(6,2),
new Point(6,1));
public Test(){
List<Point> c = valsSorted.stream()
.collect(Collectors.groupingBy(Point::getX))
.values()
.stream()
.map(j -> j.get(0))
.collect(Collectors.toList());
for(int i=0; i < c.size(); i++){
System.out.println(c.get(i));
}
}
public static void main(String []args){
Test t = new Test()
}
}
我决定使用点类,并将ID字段表示为x,将版本号表示为Y。因此,如果从那里创建流并按ID对其进行分组,则可以调用返回列表集合Collection<List<Point>>
的values方法。然后,您可以调用该集合的流,并从每个列表中获取第一个值,根据您的规范,该值按版本号降序排列,因此它应该是最高的版本号。从那里,你所要做的就是将它们收集到一个列表、数组或任何你认为必要的东西中,并根据需要进行分配。
这里唯一的问题是它们打印出了问题。不过,这应该是一个简单的解决方案。