一.()
distinct()是Stream接口的方法。distinct()使用hashCode()和equals()方法来获取不同的元素。因此,我们的类必须实现hashCode()和equals()方法。
①对包含重复元素的字符串数据类型列表去重
public static void main(String[] args) {
List<String> list = Arrays.asList("A", "B", "C", "B", "C", "A", "A");
long num = list.stream().distinct().count();
System.out.println("No. of distinct elements:" + num);
String text = list.stream().distinct().collect(Collectors.joining(","));
}
/**
输出结果:
No. of distinct elements:3
A,B,C
**/
②对对象列表去重,按整个对象去重,但该对象需重写hashCode和equals方法
在Java中,为了让对象在集合中能够更高效地进行查找和比较,我们通常需要重写对象的equals()和hashCode()方法。其中,equals()方法用于比较两个对象是否相等,而hashCode()方法则用于返回对象哈希值,供集合类使用。
默认情况下,Java会根据每个对象的内存地址来计算哈希值,因此如果两个对象在内存中的位置不同,它们的哈希值也会不同。但是,在实际开发中,我们可能需要比较的是对象的属性值而不是内存地址,这时就需要自己来实现hashCode()方法了。
虽然默认实现的hashCode()方法可以满足基本的哈希表需求,但是它有一个很大的问题:它只是返回对象的内存地址的哈希码,这意味着两个内容完全相同的对象在哈希表中还是会被认为是不同的对象,这样就会浪费大量的空间和时间。在实际使用中,这可能会导致一些问题,比如无法正确识别集合中的重复元素。但是一些标准Java类库中的类(例如String、Integer等)已经重写了hashCode()方法,以便让具有相同属性值的对象具有相同的哈希码。
要重新实现hashCode()方法,我们需要结合对象的属性值来计算哈希码,以便让具有相同属性值的对象具有相同的哈希码。一般来说,可以采用以下步骤:
把对象的非零属性用一个质数(比如31)进行加权,并把它们相加。 如果属性是布尔型,则使用(f ? 1 : 0) 的形式转换成数值型。 如果属性是浮点型,则使用(f)的方式把它们转换成整型。 如果属性是双精度型,则使用(f)的方式把它们转换成长整型,并对其进行异或操作。 如果属性是数组,则对每个元素进行递归处理。
public class Book {
private String name;
private int price;
public Book(String name, int price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public int getPrice() {
return price;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", price=" + price +
"}";
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
final Book book = (Book) obj;
if (this == book) {
return true;
} else {
return (this.name.equals(book.name) && this.price == book.price);
}
}
@Override
public int hashCode() {
int hashno = 7;
hashno = 13 * hashno + (name == null ? 0 : name.hashCode());
hashno = 13 * hashno + price;
return hashno;
// return (name,price);
}
}
public static void main(String[] args) {
List<Book> list = new ArrayList<>();
{
list.add(new Book("Java", 300));
list.add(new Book("Java", 300));
list.add(new Book("DB", 100));
list.add(new Book("Spring", 200));
list.add(new Book("Spring", 200));
}
long l = list.stream().distinct().count();
System.out.println("No. of distinct books:"+l);
list.stream().distinct().forEach(b -> System.out.println(b.getName()+ "," + b.getPrice()));
}
}
/**
输出结果:
No. of distinct books:3
Java,300
DB,100
Spring,200
**/
③按照属性对对象列表进行去重的直接实现,指定某个字段去重(强烈推荐)
方法来源
//写一个distinctByKey方法
static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Map<Object,Boolean> seen = new ConcurrentHashMap<>();
return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
public static void main(String[] args) {
List<Book> list = new ArrayList<>();
{
list.add(new Book("Java", 200));
list.add(new Book("DB", 100));
list.add(new Book("Spring", 200));
list.add(new Book("Spring", 200));
}
list.stream().filter(distinctByKey(b -> b.getName()))
.forEach(b -> System.out.println(b.getName()+ "," + b.getPrice()));
}
/**
输出结果:
Java,200
DB,100
Spring,200
**/
④指定根据某个属性去重
public static void main(String[] args) {
List<Book> list = new ArrayList<>();
{
list.add(new Book("Java", 300));
list.add(new Book("Java", 200));
list.add(new Book("Java", 200));
list.add(new Book("DB", 100));
list.add(new Book("Spring", 200));
list.add(new Book("Spring", 100));
}
//方法一
TreeSet<Book> books = new TreeSet<>(Comparator.comparing(s -> s.getPrice()));
for (Book book : list) {
books.add(book);
}
new ArrayList<>(books).forEach(System.out::println);
//方法二
list.stream().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(s -> s.getPrice()))), ArrayList::new)).forEach(System.out::println);
}
/**
输出结果:
Book{name='DB', price=100}
Book{name='Java', price=200}
Book{name='Java', price=300}
**/