I have below bean 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;
}
}
I need to store these result in HashSet
.Actually i dont want to store time
twice. if it repeats I need to either ignore or overwrite(Both will work ,its POC). Thats why I took time in equals
and hashCode
method.
I am writing HashSet
code like below.
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;
}
I am storing element in List
because I need data in sorted order. is there anyway to improve this.
in below output last 2 values are getting repeated.
[
{
"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
}
]
You are working too hard.
equals
& hashCode
.List
later. We can keep the elements ordered in certain kinds of sets.Key code:
NavigableSet < ElectricityReading > readings =
new TreeSet <>(
Comparator.comparing( ElectricityReading :: time )
)
;
For brevity, I will use the records feature in Java 16+ to briefly write the class. You could just as well use a conventional class.
record ElectricityReading( Instant time , BigDecimal readingKwh ) { }
NavigableSet
If you want a set to keep its values in a certain order, use a NavigableSet
(or SortedSet
). Java comes with a few implementations of NavigableSet
, one of which is TreeSet
.
When constructing the TreeSet
, pass 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
method that takes a method reference. We use our getter method for accessing our Instant
field, ElectricityReading#time
(implicitly created by the compiler in a record), as our method reference for comparisons.
NavigableSet < ElectricityReading > readings = new TreeSet <>( Comparator.comparing( ElectricityReading :: time ) );
Let's try that out with some sample data. We set up the data intentionally to be out-of-order to verify our set is sorting properly. Notice both the Instant
values and the readingKwh
values are increasing in parallel.
I modified your input data, for easier reading.
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" ) ) );
You also asked to prevent elements in the set that have same 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
.
Verify by dumping to console.
System.out.println( "readings = " + readings );
When run.
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]]
We see two effects, both desired:
Instant
field.1.7
reading. Our TreeSet
collection rejected our attempt to add the object with -666
, as its Instant
value was already found in an existing element of the NavigableSet
.Pulling all that code together for your copy-paste convenience.
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 );