Java根据指定字段实现对对象进行去重的五种方法
目录
- 引入问题
- 方法一:使用 HashSet 数据结构
- 方法二:使用 Java 8 的 Stream API 的 distinct() 去重
- 方法三:使用 Map 数据结构
- 方法四:使用 Collectors.toMap() 方法
- 方法五:使用 Collectors.collectingAndThen() 方法
引入问题
首先,我自定义了一个名为 Person
的 Java 类:
public class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public int hashCode() { return super.hashCode(); } @Override public boolean equals(Object obj) { return super.equals(obj); } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
Person
类中有两个属性:name
和 age
和一个全参构造方法:
name
是一个字符串类型的变量,用于表示人的姓名;age
是一个整数类型的变量,用于表示人的年龄。- 构造方法用于创建
Person
类的对象。
并且重写了三个方法:hashCode()
、equals()
和 toString()
:
hashCode()
方法返回对象的哈希码,此处直接调用了父类Object
的hashCode()
方法。equals()
方法用于比较对象是否相等,此处直接调用了父类Object
的equals()
方法。toString()
方法返回一个描述该对象内容的字符串,格式为"Person{name='姓名', age=年龄}"
。
最终,我们需要根据 Person
类的 name
字段对目标集合进行去重:
public static void main(String[] args) { List<Person> persons = new ArrayList<>(); persons.add(new Person("Tom", 20)); persons.add(new Person("Jerry", 18)); persons.add(new Person("Tom", 22)); persons.add(new Person("Jim", 23)); persons.add(new Person("Tom", 22)); persons.forEach(System.out::println); }
方法一:使用 HashSet 数据结构
根据 Java 对象某个字段进行去重,可以使用 HashSet
数据结构。HashSet
内部实现了哈希表,能够快速判断元素是否已存在,从而实现去重。
Tips: HashSet 是如何实现元素去重的,或者说如何判断元素是否重复?
在 Java 中,HashSet 是一种基于哈希表实现的集合类,它内部维护了一个存储元素的哈希表。HashSet 通过元素的哈希码(hashcode)来判断元素是否重复的。
当我们向 HashSet 中添加元素时,HashSet 会首先计算该元素的哈希码,并根据哈希码将元素放入对应的桶中。如果该桶中已经有了相同哈希码的元素,则会调用元素的 equals() 方法,比较元素是否相等。如果相等,则认为该元素已经存在于 HashSet 中,不进行重复添加;否则将该元素添加到集合中。
如果我们通过 HashSet 进行去重,就需要正确地实现 hashCode() 和 equals() 方法。hashCode() 方法应该返回与元素属性相关的哈希码,而 equals() 方法应该根据元素属性判断元素是否相等。只有这样才能保证在 HashSet 中正确地去重和查找元素。
使用 HashSet
去重的具体步骤如下:
- 重写对象的
equals
和hashCode
方法。在这两个方法中,分别比较对象的指定字段,并返回相应的哈希值。 - 创建一个
HashSet
对象,并将所有要去重的对象添加到该HashSet
中。 - 遍历该
HashSet
,处理去重后的结果。
重写 Person
类的 pythonequals
和 hashCode
方法,用于比较指定字段:
public class Person { ...... // 重写 hashCode 方法 @Override public int hashCode() { // 哈希值只与 name 字段有关 python return name.hashCode(); } // 重写 equals 方法 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; // 比较 name 字段 return name.equals(person.name); } }
在重写的 equals()
方法中,首先使用 this == o
来判断两个对象是否为同一个对象(即内存地址是否相同),如果是,则直接返回 true
。如果不是同一个对象,则继续比较其他属性。
接着,使用 o == null
判断传入的参数是否为 null
,如果是 null
,则两个对象肯定不相等,直接返回 false
。然后使用 getClass()
方法来获取传入对象的类,判断其是否与当前对象的类相同,如果不同,则两个对象肯定不相等,直接返回 false
。
最后,将参数对象强制转换成 Person
类型,并比较两个对象的 name
属性是否相等。如果相等,则认为两个对象相等,返回 true
,否则返回 false
。
同时重写 hashCode()
方法,以确保两个对象相等时它们的哈希码也相等。
使用 HashSet
去重:
public static void main(String[] args) { ...... HashSet<Person> personHashSet = new HashSet<>(persons); personHashSet.forEach(System.out::println); }
去重结果为:
Person{name='Tom', age=20} Person{name='Jerry', age=18} Person{name='Jim', age=23}
方法二:使用 Java 8 的 Stream API 的 distinct() 去重
Java 8 增加的 Stream API 提供了 distinct()
方法去重。
Stream 流的 distinct()
方法是基于对象的 equals()
方法来判断对象是否相等,因此我们需要重写 Person
类的 equals()
方法:
public class Person { ...... // 重写 equals 方法 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; // 比较 name 字段 return name.equals(person.name); } }
之后调用 stream()
方法将列表转换为流,并且使用 distinct()
方法基于 name
字段进行去重:
List<Person> collect = persons.stream() .distinct() .collect(Collectors.toList());
最后打印去重后的 Person
集合:
Person{name='Tom', age=20} Person{name='Jerry', age=18} Person{name='Jim', age=23}
方法三:使用 Map 数据结构
Java 中的 Map 是一种用于存储键值对的集合。我们可以利用 Map 中的键唯一的特性实现去重。
我们只需要遍历 List 中的 Person 对象,将 name 作为 key,Person 对象作为 value 存入 Map 中,这样就可以去除重复的 name 对应的 Person 对象:
public static void main(String[] args) { ...... Map<String, Person> map = new HashMap<>(); for (Person person : persons) { map.put(person.getName(), person); } }
去重结果如下:
Person{name='Tom', age=22} Person{name='Jerry', age=18} Person{name='Jim', age=23}
方法四:使用 Collectors.toMap() 方法
Collectors.toMap()
是 Java 8 中的一个收集器(Collector),它可以将 Stream 中的元素收集到一个 Map 中,其中每个元素都是一个键值对。该方法有多个重载形式:
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper)
将 Stream 中的元素转换为键值对,并存储到一个Map中。其中,keyMapper
用于指定如何从元素中提取键,valueMapper
用于指定如何从元素中提取值。
如果存在重复的键,则会抛出IllegalStateException
异常。
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction)
与第一种形式类似,但当存在重复的键时,会使用mergeFunction
函数来处理冲突。例如,可以使用mergeFunction
来选择较小或较大的值,或将两个值合并成一个新值。
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier)
与第二种形式类似,但允许指定用于创建 Map 的具体实现类。
使用第二种重载形式将包含Person
对象的List
转换为一个以Person
对象的姓名作为键的Map
:
public static void main(String[] args) { ...... Map<String, Person> collect = persons.stream() .collect(Collectors.toMap(Person::getName, p -> p, (p1, p2) -> p1)); }
Person::getName
:函数式接口Function
类型的方法引用,用于将Person
对象的姓名作为键。person -> person
:Lambda 表达式,用于将Perspythonon
对象本身作为值。(p1, p2) -> p1
:Lambda 表达式,用于处理当存在重复键时的情况。此处选择保留第一个键对应的值,而忽略第二个键对应的值。
去重结果如下:
Person{name='Tom', age=20} Person{name='Jerry', age=18} Person{name='Jim', age=23}
方法五:使用 Collectors.collectingAndThen() 方法
Collectors.collectingAndThen()
是 Java 8 中的一个收集器(Collector)方法,它允许在收集元素后应用一个最终转换函数。在使用collectingAndThen()
时,先通过一个初始的收集器将元素收集起来,然后再应用一个最终转换函数对收集结果进行处理。
以下是collectingAndThen()
方android法的常用重载形式:
collectingAndThen(Collector<T, A, R> downstream, Function<R, RR> finisher)
downstream
:初始的收集器,用于将元素收集起来并生成一个中间结果。finisher
:最终转换函数,用于对中间结果进行处理,并返回最终结果。
使用collectingAndThen()
方法实现去重并返回去重后的结果集:
public static void main(String[] args) { ...... ArrayList<Person> collect = persons.stream() .collect(Collectors.collectingAndThen( Collectors.toMap(Person::getName, person -> person, (p1, p2) -> p1), map -> new ArrayList<>(map.values()) )); }
- 使用
Collectors.toMap()
将persons
流中的元素转换为一个以name
作为键的Map
。 - 通过
map -> new ArrayList<>(map.values())
将Map
的值部分提取出来,并使用ArrayList
的构造函数将其包装pdpONh为一个新的ArrayList<Person>
对象。最终得到的ArrayList<Person>
对象即为去重后的结果集。
输出去重后的结果:
Person{name='Tom', age=20} Person{name='Jerry', age=18} Person{name='Jim', age=23}
以上就是Java根据指定字段实现对对象进行去重的五种方法的详细内容,更多关于Java指定字段对对象去重的资料请关注编程客栈(www.devze.com)其它相关文章!
精彩评论