我的逻辑步骤:
但是,结果并没有执行明显的逻辑(步骤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]
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
。