集合去重复那些事(三)
接上一篇: https://my.oschina.net/dtz/blog/4482631
留的一个问题解答如下
反例:本方法是串行条件过滤,不是两个参数同时成立
userList.stream().filter(
DistinctUtils.distinctBykey(User::getName)
.and(DistinctUtils.distinctBykey(User::getAddr)))
.forEach(p->{
System.err.println(p.getName()+","+p.getAddr());
});
方法一:有点笨,但是也管用
for (int i = 0; i < userList.size(); i++) {
for (int j = 0;j<userList.size(); j++) {
if (i!=j && userList.get(j).getName().equals(userList.get(i).getName())
&&userList.get(j).getAddr().equals(userList.get(i).getAddr())) {
//删除重复元素
userList.remove(userList.get(j));
}
}
}
方法二:stream+filter+map,解决方案就是多个参数拼接成一个参数
userList.stream()
.filter(p->DistinctUtils.distinctBykey(p.getName()+p.getAddr()).test(p))
.collect(Collectors.toList());
方法三:stream+filter+map,解决方案就是多个参数拼接成一个参数
userList.stream()
.filter(DistinctUtils.distinctBykey(user->user.getName()+user.getAddr()))
.collect(Collectors.toList());
方法四:stream+TreeSet,解决方案就是多个参数拼接成一个参数
userList.stream().collect(Collectors.collectingAndThen(
Collectors.toCollection(() -> new TreeSet<>(
Comparator.comparing(user -> user.getAddr() + ";" + user.getName()))), ArrayList::new));
附录信息:提供工具方法和实体类JDK源码如下:
/**
* @author dingzhen
*/
public class DistinctUtils {
public static <T> Predicate<T> distinctBykey(Function<? super T,?> keyFunction){
Map<Object, Boolean> objectMap = new ConcurrentHashMap<>();
return t -> objectMap.putIfAbsent(keyFunction.apply(t), Boolean.TRUE) == null;
}
public static <T> Predicate<T> distinctBykey(String keyFunction){
Map<Object, Boolean> objectMap = new ConcurrentHashMap<>();
return t -> objectMap.putIfAbsent(keyFunction, Boolean.TRUE) == null;
}
}
首先是filter方法,返回一个流,需要一个Predicate类型的参数(Predicate这个函数式接口,接受一个参数,返回布尔值)。
Stream<T> filter(Predicate<? super T> predicate)
filter根据Predicate返回的布尔值来判断是否要过滤掉,会过滤掉返回值为false的数据。自己定义的distinctByKey返回值就是Predicate,
所以可以作为参数传入filter。distinctByKey也需要一个Function的参数。
distinctByKey先是定义了一个线程安全的Map(相比于Hashtable以及Collections.synchronizedMap(),
ConcurrentHashMap在线程安全的基础上提供了更好的写并发能力,但同时降低了对读一致性的要求),
因为在流计算中是多线程处理的,需要线程安全。然后将值作为key,TRUE作为value put到map中。
这里的put方法使用的是putIfAbsent()。putIfAbsent()方法是如果key不存在则put如map中,并返回null。
若key存在,则直接返回key所对应的value值。
所以 seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
这里用 == null判断是否存在于map了,若存在则返回false,不存在则为true。以用来达到去重的目的。
default V putIfAbsent(K key, V value) {
V v = get(key);
if (v == null) {
v = put(key, value);
}
return v;
}
注:putIfAbsent()定义在Map接口中,是默认方法。
源码:
/**
* Represents a predicate (boolean-valued function) of one argument.
*
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #test(Object)}.
*
* @param <T> the type of the input to the predicate
*
* @since 1.8
*/
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
/**
* Represents a function that accepts one argument and produces a result.
*
* <p>This is a <a href="package-summary.html">functional interface</a>
* whose functional method is {@link #apply(Object)}.
*
* @param <T> the type of the input to the function
* @param <R> the type of the result of the function
*
* @since 1.8
*/
@FunctionalInterface
public interface Function<T, R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
/**
* If the specified key is not already associated with a value (or is mapped
* to {@code null}) associates it with the given value and returns
* {@code null}, else returns the current value.
*
* @implSpec
* The default implementation is equivalent to, for this {@code
* map}:
*
* <pre> {@code
* V v = map.get(key);
* if (v == null)
* v = map.put(key, value);
*
* return v;
* }</pre>
*
* <p>The default implementation makes no guarantees about synchronization
* or atomicity properties of this method. Any implementation providing
* atomicity guarantees must override this method and document its
* concurrency properties.
*
* @param key key with which the specified value is to be associated
* @param value value to be associated with the specified key
* @return the previous value associated with the specified key, or
* {@code null} if there was no mapping for the key.
* (A {@code null} return can also indicate that the map
* previously associated {@code null} with the key,
* if the implementation supports null values.)
* @throws UnsupportedOperationException if the {@code put} operation
* is not supported by this map
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
* @throws ClassCastException if the key or value is of an inappropriate
* type for this map
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
* @throws NullPointerException if the specified key or value is null,
* and this map does not permit null keys or values
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
* @throws IllegalArgumentException if some property of the specified key
* or value prevents it from being stored in this map
* (<a href="{@docRoot}/java/util/Collection.html#optional-restrictions">optional</a>)
* @since 1.8
*/
default V putIfAbsent(K key, V value) {
V v = get(key);
if (v == null) {
v = put(key, value);
}
return v;
}
package com.dingzhen;
/**
* @author dingzhen
*/
public class User {
private String name;
private Integer age;
private String addr;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
}
以上是 集合去重复那些事(三) 的全部内容, 来源链接: utcz.com/z/519276.html