按排序顺序将自定义 class 的数据存储在 HashSet 中
Storing Data of custom class in HashSet in sorted order
我有下面的豆子class
public class ElectricityReading {
private Instant time;
private BigDecimal reading; // kW
public ElectricityReading() { }
public ElectricityReading(Instant time, BigDecimal reading) {
this.time = time;
this.reading = reading;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ElectricityReading that = (ElectricityReading) o;
return Objects.equals(time, that.time);
}
@Override
public int hashCode() {
return Objects.hash(time);
}
public BigDecimal getReading() {
return reading;
}
public Instant getTime() {
return time;
}
public void setReading(BigDecimal reading){
this.reading=reading;
}
}
我需要将这些结果存储在 HashSet
中。实际上我不想存储 time
两次。如果它重复我需要忽略或覆盖(两者都可以工作,它的 POC)。这就是我花时间研究 equals
和 hashCode
方法的原因。
我正在编写如下 HashSet
代码。
public List<ElectricityReading> generate(int number) {
Set<ElectricityReading>reading= new HashSet<>();
Instant now = Instant.now();
Random readingRandomiser = new Random();
for (int i = 0; i < number; i++) {
double positiveRandomValue = Math.abs(readingRandomiser.nextGaussian());
BigDecimal randomReading = BigDecimal.valueOf(positiveRandomValue).setScale(4, RoundingMode.CEILING);
ElectricityReading electricityReading = new ElectricityReading(now.minusSeconds(i * 10), randomReading);
if(!reading.contains(electricityReading.getTime())){
reading.add(electricityReading);
}else {
electricityReading.setReading(electricityReading.getReading());
}
}
List<ElectricityReading> readings = new ArrayList<>(reading);
readings.sort(Comparator.comparing(ElectricityReading::getTime));
return readings;
}
我将元素存储在 List
中,因为我需要按排序顺序排列数据。无论如何要改善这一点。
在下面的输出中,最后 2 个值正在重复。
[
{
"time": "2021-09-15T20:13:51.268560800Z",
"reading": 2.5574
},
{
"time": "2020-11-29T08:00:00Z",
"reading": 1.7
},
{
"time": "2020-11-29T08:00:00Z",
"reading": 1.7
}
]
有一个名为 LinkedHashSet
的 Java class 可以让您按照插入的顺序迭代项目。如果您希望它们以随机顺序插入,然后按排序顺序迭代,排序将不得不在某处 发生。在这种情况下,像 TreeSet
这样的有序集合可能是更好的选择?
您实际上不需要集合,因为您有一个局部变量 now
并且您正在从中减去 i * 10
秒,您将不会有重复的时间条目。在实际实现中不做太多更改,只需执行以下操作:
public List<ElectricityReading> generate(int number) {
List<ElectricityReading> readings = new ArrayList<>();
Instant now = Instant.now();
Random readingRandomiser = new Random();
for (int i = 0; i < number; i++) {
double positiveRandomValue = Math.abs(readingRandomiser.nextGaussian());
BigDecimal randomReading = BigDecimal.valueOf(positiveRandomValue).setScale(4, RoundingMode.CEILING);
ElectricityReading electricityReading = new ElectricityReading(now.minusSeconds(i * 10), randomReading);
readings.add(electricityReading);
}
readings.sort(Comparator.comparing(ElectricityReading::getTime));
return readings;
}
tl;博士
你太辛苦了
- 无需覆盖任何方法,例如
equals
& hashCode
。
- 以后不用做
List
了。我们可以让元素按特定类型的集合排序。
- 如果我们指定比较器,使用有序集会自动消除重复项。
关键代码:
NavigableSet < ElectricityReading > readings =
new TreeSet <>(
Comparator.comparing( ElectricityReading :: time )
)
;
详情
为简洁起见,我将使用Java 16+中的records特性来简单写下class。您也可以使用传统的 class.
record ElectricityReading( Instant time , BigDecimal readingKwh ) { }
NavigableSet
如果您希望集合按特定顺序保存其值,请使用 NavigableSet
(or SortedSet
). Java comes with a few implementations of NavigableSet
, one of which is TreeSet
。
构造TreeSet
时,传递采用方法引用的Comparator
so the navigable set knows how you want the sorting performed. Fortunately, modern Java makes creating a comparator quite easy with the Comparator.comparing
方法。我们使用我们的 getter 方法来访问我们的 Instant
字段,ElectricityReading#time
(由编译器在记录中隐式创建),作为我们比较的方法参考。
NavigableSet < ElectricityReading > readings = new TreeSet <>( Comparator.comparing( ElectricityReading :: time ) );
让我们用一些示例数据来尝试一下。我们故意将数据设置为无序,以验证我们的集合是否正确排序。请注意 Instant
值和 readingKwh
值都在并行增加。
为了便于阅读,我修改了您的输入数据。
readings.add( new ElectricityReading( Instant.parse( "2021-09-05T08:00:00Z" ) , new BigDecimal( "1.7" ) ) );
readings.add( new ElectricityReading( Instant.parse( "2021-09-05T08:00:00Z" ) , new BigDecimal( "-666" ) ) ); // Repeated `Instant` value.
readings.add( new ElectricityReading( Instant.parse( "2021-11-05T09:00:00Z" ) , new BigDecimal( "3.1" ) ) ); // Out-of-order.
readings.add( new ElectricityReading( Instant.parse( "2021-10-05T09:00:00Z" ) , new BigDecimal( "2.5574" ) ) );
您还要求阻止集合中具有相同 Instant
value, without regard to the BigDecimal
value. So let's repeat that first data element. We use a special value for readingKwh
, -666
, so we can observe the behavior of TreeSet#add
.
的元素
通过转储到控制台进行验证。
System.out.println( "readings = " + readings );
当运行.
readings = [ElectricityReading[time=2021-09-05T08:00:00Z, readingKwh=1.7], ElectricityReading[time=2021-10-05T09:00:00Z, readingKwh=2.5574], ElectricityReading[time=2021-11-05T09:00:00Z, readingKwh=3.1]]
我们看到两种效果,都是需要的:
- 集合中的元素按
Instant
字段的时间顺序迭代。
- 重复的对象被忽略了,因为我们看到集合保持
1.7
读数。我们的 TreeSet
集合拒绝了我们使用 -666
添加对象的尝试,因为它的 Instant
值已经在 NavigableSet
. 的现有元素中找到
为了方便复制粘贴,将所有代码放在一起。
record ElectricityReading( Instant time , BigDecimal readingKwh ) { }
NavigableSet < ElectricityReading > readings = new TreeSet <>( Comparator.comparing( ElectricityReading :: time ) );
readings.add( new ElectricityReading( Instant.parse( "2021-09-05T08:00:00Z" ) , new BigDecimal( "1.7" ) ) );
readings.add( new ElectricityReading( Instant.parse( "2021-09-05T08:00:00Z" ) , new BigDecimal( "-666" ) ) ); // Repeated `Instant` value.
readings.add( new ElectricityReading( Instant.parse( "2021-11-05T09:00:00Z" ) , new BigDecimal( "3.1" ) ) ); // Out-of-order.
readings.add( new ElectricityReading( Instant.parse( "2021-10-05T09:00:00Z" ) , new BigDecimal( "2.5574" ) ) );
System.out.println( "readings = " + readings );
我有下面的豆子class
public class ElectricityReading {
private Instant time;
private BigDecimal reading; // kW
public ElectricityReading() { }
public ElectricityReading(Instant time, BigDecimal reading) {
this.time = time;
this.reading = reading;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ElectricityReading that = (ElectricityReading) o;
return Objects.equals(time, that.time);
}
@Override
public int hashCode() {
return Objects.hash(time);
}
public BigDecimal getReading() {
return reading;
}
public Instant getTime() {
return time;
}
public void setReading(BigDecimal reading){
this.reading=reading;
}
}
我需要将这些结果存储在 HashSet
中。实际上我不想存储 time
两次。如果它重复我需要忽略或覆盖(两者都可以工作,它的 POC)。这就是我花时间研究 equals
和 hashCode
方法的原因。
我正在编写如下 HashSet
代码。
public List<ElectricityReading> generate(int number) {
Set<ElectricityReading>reading= new HashSet<>();
Instant now = Instant.now();
Random readingRandomiser = new Random();
for (int i = 0; i < number; i++) {
double positiveRandomValue = Math.abs(readingRandomiser.nextGaussian());
BigDecimal randomReading = BigDecimal.valueOf(positiveRandomValue).setScale(4, RoundingMode.CEILING);
ElectricityReading electricityReading = new ElectricityReading(now.minusSeconds(i * 10), randomReading);
if(!reading.contains(electricityReading.getTime())){
reading.add(electricityReading);
}else {
electricityReading.setReading(electricityReading.getReading());
}
}
List<ElectricityReading> readings = new ArrayList<>(reading);
readings.sort(Comparator.comparing(ElectricityReading::getTime));
return readings;
}
我将元素存储在 List
中,因为我需要按排序顺序排列数据。无论如何要改善这一点。
在下面的输出中,最后 2 个值正在重复。
[
{
"time": "2021-09-15T20:13:51.268560800Z",
"reading": 2.5574
},
{
"time": "2020-11-29T08:00:00Z",
"reading": 1.7
},
{
"time": "2020-11-29T08:00:00Z",
"reading": 1.7
}
]
有一个名为 LinkedHashSet
的 Java class 可以让您按照插入的顺序迭代项目。如果您希望它们以随机顺序插入,然后按排序顺序迭代,排序将不得不在某处 发生。在这种情况下,像 TreeSet
这样的有序集合可能是更好的选择?
您实际上不需要集合,因为您有一个局部变量 now
并且您正在从中减去 i * 10
秒,您将不会有重复的时间条目。在实际实现中不做太多更改,只需执行以下操作:
public List<ElectricityReading> generate(int number) {
List<ElectricityReading> readings = new ArrayList<>();
Instant now = Instant.now();
Random readingRandomiser = new Random();
for (int i = 0; i < number; i++) {
double positiveRandomValue = Math.abs(readingRandomiser.nextGaussian());
BigDecimal randomReading = BigDecimal.valueOf(positiveRandomValue).setScale(4, RoundingMode.CEILING);
ElectricityReading electricityReading = new ElectricityReading(now.minusSeconds(i * 10), randomReading);
readings.add(electricityReading);
}
readings.sort(Comparator.comparing(ElectricityReading::getTime));
return readings;
}
tl;博士
你太辛苦了
- 无需覆盖任何方法,例如
equals
&hashCode
。 - 以后不用做
List
了。我们可以让元素按特定类型的集合排序。 - 如果我们指定比较器,使用有序集会自动消除重复项。
关键代码:
NavigableSet < ElectricityReading > readings =
new TreeSet <>(
Comparator.comparing( ElectricityReading :: time )
)
;
详情
为简洁起见,我将使用Java 16+中的records特性来简单写下class。您也可以使用传统的 class.
record ElectricityReading( Instant time , BigDecimal readingKwh ) { }
NavigableSet
如果您希望集合按特定顺序保存其值,请使用 NavigableSet
(or SortedSet
). Java comes with a few implementations of NavigableSet
, one of which is TreeSet
。
构造TreeSet
时,传递采用方法引用的Comparator
so the navigable set knows how you want the sorting performed. Fortunately, modern Java makes creating a comparator quite easy with the Comparator.comparing
方法。我们使用我们的 getter 方法来访问我们的 Instant
字段,ElectricityReading#time
(由编译器在记录中隐式创建),作为我们比较的方法参考。
NavigableSet < ElectricityReading > readings = new TreeSet <>( Comparator.comparing( ElectricityReading :: time ) );
让我们用一些示例数据来尝试一下。我们故意将数据设置为无序,以验证我们的集合是否正确排序。请注意 Instant
值和 readingKwh
值都在并行增加。
为了便于阅读,我修改了您的输入数据。
readings.add( new ElectricityReading( Instant.parse( "2021-09-05T08:00:00Z" ) , new BigDecimal( "1.7" ) ) );
readings.add( new ElectricityReading( Instant.parse( "2021-09-05T08:00:00Z" ) , new BigDecimal( "-666" ) ) ); // Repeated `Instant` value.
readings.add( new ElectricityReading( Instant.parse( "2021-11-05T09:00:00Z" ) , new BigDecimal( "3.1" ) ) ); // Out-of-order.
readings.add( new ElectricityReading( Instant.parse( "2021-10-05T09:00:00Z" ) , new BigDecimal( "2.5574" ) ) );
您还要求阻止集合中具有相同 Instant
value, without regard to the BigDecimal
value. So let's repeat that first data element. We use a special value for readingKwh
, -666
, so we can observe the behavior of TreeSet#add
.
通过转储到控制台进行验证。
System.out.println( "readings = " + readings );
当运行.
readings = [ElectricityReading[time=2021-09-05T08:00:00Z, readingKwh=1.7], ElectricityReading[time=2021-10-05T09:00:00Z, readingKwh=2.5574], ElectricityReading[time=2021-11-05T09:00:00Z, readingKwh=3.1]]
我们看到两种效果,都是需要的:
- 集合中的元素按
Instant
字段的时间顺序迭代。 - 重复的对象被忽略了,因为我们看到集合保持
1.7
读数。我们的TreeSet
集合拒绝了我们使用-666
添加对象的尝试,因为它的Instant
值已经在NavigableSet
. 的现有元素中找到
为了方便复制粘贴,将所有代码放在一起。
record ElectricityReading( Instant time , BigDecimal readingKwh ) { }
NavigableSet < ElectricityReading > readings = new TreeSet <>( Comparator.comparing( ElectricityReading :: time ) );
readings.add( new ElectricityReading( Instant.parse( "2021-09-05T08:00:00Z" ) , new BigDecimal( "1.7" ) ) );
readings.add( new ElectricityReading( Instant.parse( "2021-09-05T08:00:00Z" ) , new BigDecimal( "-666" ) ) ); // Repeated `Instant` value.
readings.add( new ElectricityReading( Instant.parse( "2021-11-05T09:00:00Z" ) , new BigDecimal( "3.1" ) ) ); // Out-of-order.
readings.add( new ElectricityReading( Instant.parse( "2021-10-05T09:00:00Z" ) , new BigDecimal( "2.5574" ) ) );
System.out.println( "readings = " + readings );