在Java流中使用distinct时获取重复元素

问题描述 投票:0回答:1

我的逻辑步骤:

  1. 生成流列表
  2. 将每个元素映射到列表流
  3. 将列表流收集到一个流
  4. 独特元素
  5. Map 函数应用于每个元素并将结果收集到列表中

但是,结果并没有执行明显的逻辑(步骤4),这是我无法理解的。

public class test {
    public static void main(String[] args) {
        List<TestBean> obj_list = Arrays.asList(new TestBean("aa"), new TestBean("bb" ), new TestBean("bb")).stream()
                .distinct().map(tt -> {
                    tt.col = tt.col + "_t";
                    return tt;
                }).collect(Collectors.toList());
        System.out.println(obj_list);

        List<String> string_obj_list = Arrays.asList(new String("1"), new String("2"), new String("2")).stream().distinct().map(t -> t + "_t").collect(Collectors.toList());
        System.out.println(string_obj_list);

        List<String> string_list = Arrays.asList("1", "2", "2").stream().distinct().map(t -> t + "_t").collect(Collectors.toList());
        System.out.println(string_list);
    }
}

@Data
@AllArgsConstructor
@EqualsAndHashCode
class TestBean {
    String col;
}

结果如下,第一行我理解是异常的:

[TestBean(col=aa_t), TestBean(col=bb_t), TestBean(col=bb_t)]
[1_t, 2_t]
[1_t, 2_t]
java java-stream distinct
1个回答
0
投票

distinct()
操作正在使用
Object.equals(Object)
过滤流中的项目集。

但是,您在流式传输项目时会对其进行变异 - 对于

Set
操作来说这是一个非常糟糕的主意 - 因此理论上,在您的运行中,第一个
TestBean(col=bb)
在最后一个
TestBean(col=bb_t) 之前可能会更改为 
TestBean(col=bb)
 
由distinct() 处理。因此,此时
distinct()
步骤在流中看到了 3 个独特的项目,最后一个
.map()
看到了所有三个项目。

您可以通过重新处理流来验证,而不会产生“.map()”副作用,或者在

.distinct()
之后添加
.map()

从中得出的结论:不要在数据结构上使用distinct()或其他类似集合的操作来更改

equals()
hashCode()
中使用的字段,因为这会给
set.add()
操作带来误导/重复。这就是 Java 记录有用的地方,因为它们无法更改,并且可以消除这些类型的副作用带来的错误:

record TestBean(String col) {}

示例

@EqualsAndHashCode
上的
TestBean
标签正在生成
equals
hashCode
调用,这对于
Set
/
distinct()
操作的工作至关重要。如果添加项目后哈希码/等于发生更改,则该集合将无法正常工作,因为它无法将前一个元素匹配为新添加元素的重复项。考虑这个更简单的 TestBean 定义,它添加相同的实例 5 次:

public static void main(String... args) {
    class TestBean {
        String col;
        TestBean(String col) {
            this.col = col;
        }
        // This is bad choice hashCode as it changes if "col" is changed:
        public int hashCode() {
            return col.hashCode();
        }
    }
    Set<TestBean> set = new HashSet<>();
    TestBean x = new TestBean("bb");
    for (int i = 0; i < 5; i++) {
        System.out.println("set.add(x)="+set.add(x));
        System.out.println("set.size()="+set.size());
        // Comment out next line or whole hashCode method:
        x.col += "_t";
    }
}

运行上面的代码,将相同的元素添加到集合中 5 次。您将看到

set.size()
可能是 5 而不是 1。注释掉导致哈希码更改的行 - 或
hashCode()
方法,以及预期的
set.size()=1

© www.soinside.com 2019 - 2024. All rights reserved.