通过比较 Java 8 多个字段或属性,学习collect distinct objects from a stream中collect distinct objects from a stream 。
1. Distinct by multiple fields – distinctByKeys() function
下面给出的是一个接受varargs参数的函数,我们可以传递多个键提取器(我们要在其上过滤重复项的字段)。
此函数将键创建为List ,其中list包含用于检查不同组合的字段的值。 列表键插入到ConcurrentHashMap ,该键仅存储唯一且不同的键。private static Predicate distinctByKeys(Function super T, ?>... keyExtractors)
{
final Map, Boolean> seen = new ConcurrentHashMap<>();
return t ->
{
final List> keys = Arrays.stream(keyExtractors)
.map(ke -> ke.apply(t))
.collect(Collectors.toList());
return seen.putIfAbsent(keys, Boolean.TRUE) == null;
};
}
在给定的示例中,我们发现所有具有distinct ids and name记录。 我们应该只有3条记录作为输出。import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@SuppressWarnings("unchecked")
public class Main
{
public static void main(String[] args)
{
List recordsList = getRecords();
List list = recordsList
.stream()
.filter(distinctByKeys(Record::getId, Record::getName))
.collect(Collectors.toList());
System.out.println(list);
}
private static Predicate distinctByKeys(Function super T, ?>... keyExtractors)
{
final Map, Boolean> seen = new ConcurrentHashMap<>();
return t ->
{
final List> keys = Arrays.stream(keyExtractors)
.map(ke -> ke.apply(t))
.collect(Collectors.toList());
return seen.putIfAbsent(keys, Boolean.TRUE) == null;
};
}
private static ArrayList getRecords()
{
ArrayList records = new ArrayList<>();
records.add(new Record(1l, 10l, "record1", "record1@email.com", "India"));
records.add(new Record(1l, 20l, "record1", "record1@email.com", "India"));
records.add(new Record(2l, 30l, "record2", "record2@email.com", "India"));
records.add(new Record(2l, 40l, "record2", "record2@email.com", "India"));
records.add(new Record(3l, 50l, "record3", "record3@email.com", "India"));
return records;
}
}
Program output.[
Record [id=1, count=10, name=record1, email=record1@email.com, location=India],
Record [id=2, count=30, name=record2, email=record2@email.com, location=India],
Record [id=3, count=50, name=record3, email=record3@email.com, location=India]
]
2. Distinct by multiple properties – Custom key class
另一种可能的方法是使用一个自定义类,该类代表POJO类的不同键。 在我们的案例中,我们创建了一个CustomKey类,该类可以具有任意多个字段,并且列表中的不同元素将基于所有这些字段的值的不同组合来获取。
在给定的示例中,我们再次发现所有具有唯一键和名称的记录。public class CustomKey
{
private long id;
private String name;
public CustomKey(final Record record)
{
this.id = record.getId();
this.name = record.getName();
}
//Getters and setters
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + (int) (id ^ (id >>> 32));
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
CustomKey other = (CustomKey) obj;
if (id != other.id)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
让我们看看如何使用此自定义键基于给定的多个字段过滤列表中的不同元素。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@SuppressWarnings("unchecked")
public class Main
{
public static void main(String[] args)
{
List recordsList = getRecords();
List list = recordsList.stream()
.filter(distinctByKey(CustomKey::new))
.collect(Collectors.toList());
System.out.println(list);
}
public static Predicate distinctByKey(Function super T, Object> keyExtractor)
{
Map seen = new ConcurrentHashMap<>();
return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
private static ArrayList getRecords()
{
ArrayList records = new ArrayList<>();
records.add(new Record(1l, 10l, "record1", "record1@email.com", "India"));
records.add(new Record(1l, 20l, "record1", "record1@email.com", "India"));
records.add(new Record(2l, 30l, "record2", "record2@email.com", "India"));
records.add(new Record(2l, 40l, "record2", "record2@email.com", "India"));
records.add(new Record(3l, 50l, "record3", "record3@email.com", "India"));
return records;
}
}
Program output.[
Record [id=1, count=10, name=record1, email=record1@email.com, location=India],
Record [id=3, count=30, name=record2, email=record2@email.com, location=India],
Record [id=5, count=50, name=record3, email=record3@email.com, location=India]
]
作为参考,我们使用了下面给出的Record类。public class Record
{
private long id;
private long count;
private String name;
private String email;
private String location;
public Record(long id, long count, String name,
String email, String location) {
super();
this.id = id;
this.count = count;
this.name = name;
this.email = email;
this.location = location;
}
//Getters and setters
@Override
public String toString() {
return "Record [id=" + id + ", count=" + count + ", name=" + name +
", email=" + email + ", location="
+ location + "]";
}
}
Read More: