在Java 8中,如何Stream
通过检查每个对象的属性的清晰度来使用API 过滤集合?
例如,我有一个Person
对象列表,我想删除具有相同名称的人,
persons.stream().distinct();
Run Code Online (Sandbox Code Playgroud)
将使用Person
对象的默认相等检查,所以我需要像,
persons.stream().distinct(p -> p.getName());
Run Code Online (Sandbox Code Playgroud)
不幸的是,该distinct()
方法没有这种过载.如果不修改类中的相等性检查,Person
是否可以简洁地执行此操作?
我有一个流如:
Arrays.stream(new String[]{"matt", "jason", "michael"});
Run Code Online (Sandbox Code Playgroud)
我想删除以相同字母开头的名称,以便只留下以该字母开头的一个名称(无关紧要).
我试图了解该distinct()
方法的工作原理.我在文档中读到它基于对象的"等于"方法.但是,当我尝试包装String时,我注意到从不调用equals方法,也没有删除任何内容.这里有什么我想念的吗?
包装类:
static class Wrp {
String test;
Wrp(String s){
this.test = s;
}
@Override
public boolean equals(Object other){
return this.test.charAt(0) == ((Wrp) other).test.charAt(0);
}
}
Run Code Online (Sandbox Code Playgroud)
还有一些简单的代码:
public static void main(String[] args) {
Arrays.stream(new String[]{"matt", "jason", "michael"})
.map(Wrp::new)
.distinct()
.map(wrp -> wrp.test)
.forEach(System.out::println);
}
Run Code Online (Sandbox Code Playgroud) 我有一个Parent
具有20个属性的Java类(attrib1, attrib2 .. attrib20)
及其相应的getter和setter.我还有两个Parent
对象列表:list1
和list2
.
现在我想合并两个列表并避免基于attrib1
和的重复对象attrib2
.
使用Java 8:
List<Parent> result = Stream.concat(list1.stream(), list2.stream())
.distinct()
.collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)
但是我必须在哪个地方指定属性?我应该覆盖hashCode
和equals
方法吗?
让我们假设我有一个Stream<T>
并且想要只获得不同的元素并进行排序.
天真的方法是做到以下几点:
Stream.of(...)
.sorted()
.distinct()
Run Code Online (Sandbox Code Playgroud)
或者,也许相反:
Stream.of(...)
.distinct()
.sorted()
Run Code Online (Sandbox Code Playgroud)
由于JDK的源代码无法实现这两者的实现,我只是想知道可能的内存消耗和性能影响.
或者编写我自己的过滤器会更有效率如下?
Stream.of(...)
.sorted()
.filter(noAdjacentDuplicatesFilter())
public static Predicate<Object> noAdjacentDuplicatesFilter() {
final Object[] previousValue = {new Object()};
return value -> {
final boolean takeValue = !Objects.equals(previousValue[0], value);
previousValue[0] = value;
return takeValue;
};
}
Run Code Online (Sandbox Code Playgroud) 如何从对象列表中获得不同的(基于两个属性的不同)列表.例如,让我们有属性名称和价格的对象列表.现在我如何获得具有不同名称或价格的列表.
假设
list<xyz> l1 = getlist(); // getlist will return the list.
Run Code Online (Sandbox Code Playgroud)
现在让l1具有以下属性(名称,价格): -
n1,p1
n1,p2
n2,p1
n2,p3
现在在过滤器之后,列表应为-n1
,p1
n2,p3
我试着像这样解决 -
public List<xyz> getFilteredList(List<xyz> l1) {
return l1
.stream()
.filter(distinctByKey(xyz::getName))
.filter(distinctByKey(xyz::getPrice))
.collect(Collectors.toList());
}
private static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
Map<Object,Boolean> seen = new ConcurrentHashMap<>();
return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
Run Code Online (Sandbox Code Playgroud)
现在的问题是当我对名称进行过滤时,列表返回将是 -
n1,p1
n2,p1
然后它将在价格上运行过滤器返回 -
n1,p1
这不是预期的结果.
我正在查看此代码并尝试理解以下代码.
public static <T> Predicate<T> distinctByKey(Function<? super T,Object> keyExtractor) {
Map<Object,Boolean> seen = new ConcurrentHashMap<>();
return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
BigDecimal totalShare = orders.stream()
.filter(distinctByKey(o -> o.getCompany().getId()))
.map(Order::getShare)
.reduce(BigDecimal.ZERO, BigDecimal::add);
Run Code Online (Sandbox Code Playgroud)
我的问题是每次调用distinctByKey并生成新的ConcurrentHashMap.如何使用新的ConcurrentHashMap <>()来维护状态; ?
如果我有以下列表:
List<String> list = Arrays.asList("hello", "world", "hello");
Run Code Online (Sandbox Code Playgroud)
我应用以下(Java8):
list.stream().distinct().collect(Collectors.toString());
Run Code Online (Sandbox Code Playgroud)
然后我会得到一个包含“hello”和“world”的列表。
但是,就我而言,我有一个类型列表(来自外部 api),我想在其中“绕过”equals 方法,最好使用比较器,因为它没有涵盖我需要的内容。
假设这个类看起来像这样:
public class Point {
float x;
float y;
//getters and setters omitted
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,我希望将涵盖特定标准的两点定义为相等,例如 (30, 20) 和 (30.0001, 19.999)。
自定义比较器可以做到这一点,但我发现没有 API 可以执行 Java8 Stream 中的 distinct() 功能,而是使用比较器(或类似模式)。
有什么想法吗?我知道我可以编写这样的函数,但我更喜欢使用现有 apis 的优雅方式......我对外部库没有限制(番石榴、apache-commons 等,如果他们有一种舒适的方式,欢迎使用)我需要的)。
我想创建一个对流执行一些复杂操作的方法(例如替换第 7 个元素, 删除最后一个元素, 删除相邻的重复项等)而不缓存整个流。
但是什么流 api 让我插入这个方法?我是否必须创建自己的收集器,以便在收集时将项目发送到其他流?但这会改变数据流的方向,从拉到推,对吧?
这种方法的可能签名是什么?
Stream<T> process(Stream<T> in)
Run Code Online (Sandbox Code Playgroud)
可能是不可能的(在单线程代码中),因为只有在收集整个输入流后才能返回结果
另一个想法:
void process(Stream<T> in, Stream<T> out)
Run Code Online (Sandbox Code Playgroud)
似乎也有点缺陷,因为 java 不允许发出将项目插入现有流(作为out
参数提供)。
那么我如何在java中进行一些复杂的流处理?