在使用java8的stream流对List集合操作时,遇到了去重问题。原有的distinct()方法需要重写对象的equals()和hashCode()方法,比较麻烦,而且写在实体里比较难看。于是查阅资料,写了如下代码:
List<User> users = Lists.newArrayList(new User("aa",11),new User("bb",13),new User("aa",14) );
List<User> collect = users.stream().filter(distinctByKey(User::getName)).collect(Collectors.toList());
System.out.println(collect);
//去重函数
public <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Set<Object> set = ConcurrentHashMap.newKeySet();
System.out.println("----------");
return t -> set.add(keyExtractor.apply(t));
}
运行结果
----------
[User(name=aa, age=11), User(name=bb, age=13)]
巧妙的运用了set.add()方法返回boolean值来去重。根据这个思路我写了如下代码:
首先重写User类的equals()与hashCode()方法
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof User)) {
return false;
}
User user = (User) o;
return Objects.equal(getName(), user.getName());
}
@Override
public int hashCode() {
return Objects.hashCode(getName());
}
List<User> collect1 = users.stream().filter((user) -> {
Set<User> set = ConcurrentHashMap.newKeySet();
System.out.println("----------");
return set.add(user);
}).collect(Collectors.toList());
System.out.println(collect1);
运行结果
----------
----------
----------
[User(name=aa, age=11), User(name=bb, age=13), User(name=aa, age=14)]
去重失败,结果打印了三次“-----”,说明new Set()被执行了三次,每次filter都new了一个Set。那为什么distinctByKey()方法中的 Set set = ConcurrentHashMap.newKeySet()只执行了一次呢?这里,我认为虽然distinctByKey()方法作为参数传递给了filter()方法,但distinctByKey()本身就是一个方法,在执行时也有自己的方法栈。filter()方法栈只是使用了distinctByKey()方法栈的boolean类型的返回值。每次filter(),将用user.getName()值传递进distinctByKey()方法栈,入栈计算后,将栈顶的boolean结果返回给filter()方法。因此,distinctByKey()方法栈只需在创建它自己的栈的时候执行一次 ConcurrentHashMap.newKeySet()操作。lambda表达式直接当做了filter()的变量进行入栈操作了,所以会执行多次。于是我改写了代码,将new Set()放在了lambda表达式外面。
Set<User> set = ConcurrentHashMap.newKeySet();
List<User> collect1 = users.stream().filter((user) -> {
System.out.println("----------");
return set.add(user);
}).collect(Collectors.toList());
System.out.println(collect1);
运行结果
----------
----------
----------
[User(name=aa, age=11), User(name=bb, age=13)]
在不重写User类的equals()与hashCode()方法时,这样写:
Set<String> set = ConcurrentHashMap.newKeySet();
List<User> collect1 = users.stream().filter((user) -> {
System.out.println("----------");
return set.add(user.getName());
}).collect(Collectors.toList());
System.out.println(collect1);
运行结果
----------
----------
----------
[User(name=aa, age=11), User(name=bb, age=13)]