MST
星途 面试题库

面试题:Java Stream distinct方法与自定义对象去重及性能调优

给定一个自定义的Java类,类中有多个属性。现在要使用Stream的distinct方法对包含大量该自定义类对象的Stream进行去重,在保证去重逻辑正确的前提下,如何优化性能?请详细说明去重逻辑的实现以及性能优化的思路,包括可能涉及到的哈希算法调整、缓存策略等。
34.8万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试
  1. 去重逻辑实现
    • 要使用Stream.distinct方法对自定义类对象进行去重,需要在自定义类中正确重写equalshashCode方法。
    • 例如,假设有一个自定义类Person
    public class Person {
        private String name;
        private int age;
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        // 重写equals方法
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Person person = (Person) o;
            return age == person.age && Objects.equals(name, person.name);
        }
    
        // 重写hashCode方法
        @Override
        public int hashCode() {
            return Objects.hash(name, age);
        }
    }
    
    • 然后在使用Stream时,可以这样进行去重:
    List<Person> personList = Arrays.asList(
        new Person("Alice", 25),
        new Person("Bob", 30),
        new Person("Alice", 25)
    );
    List<Person> distinctList = personList.stream()
       .distinct()
       .collect(Collectors.toList());
    
  2. 性能优化思路
    • 哈希算法调整
      • 在重写hashCode方法时,尽量保证哈希值的均匀分布。对于包含多个属性的自定义类,使用Objects.hash这种综合多个属性生成哈希值的方法是比较好的选择。如果属性值的分布有一定规律,可以根据属性的特点手动调整哈希算法。例如,如果某个属性是时间戳,且时间戳的变化范围大且均匀,可以在哈希计算中给予该属性更高的权重,这样可以减少哈希冲突的概率。
    • 缓存策略
      • 局部缓存:如果流中的元素具有一定的局部性,可以使用局部缓存。例如,如果流中的元素是按照某个属性分组后依次出现的,可以在处理每个分组时,使用一个小的缓存来存储已经处理过的元素。以Person类为例,如果流中的元素是按照年龄分组后依次出现的,可以在处理每个年龄组时,使用一个Set<Person>作为缓存,在处理当前年龄组的元素时,先检查缓存中是否已经存在该元素,若存在则跳过,这样可以减少equalshashCode方法的调用次数。
      • 全局缓存:对于整个流处理过程,可以考虑使用全局缓存。如果流中的元素总量不是特别大,可以在处理流之前,先将所有元素放入一个Set中,然后再对流进行处理。这样在Stream.distinct操作时,实际上是对已经去重的集合进行操作,从而提高性能。但这种方法需要额外的内存来存储全局缓存,所以适用于流元素总量较小的情况。
    • 减少不必要计算
      • 如果在计算hashCodeequals方法中有一些复杂的计算,且这些计算结果不会随对象状态变化而变化,可以将这些计算结果缓存起来。例如,如果Person类中有一个属性是根据其他属性计算得到的复杂值,可以在对象创建时计算一次并缓存,在hashCodeequals方法中直接使用缓存值,避免重复计算。