日拱一卒:时间统计图之坐标拾取优化
2023-02-01 本文已影响0人
Tinyspot
练习:统计当天时间段内的值
横坐标值是连续有间隔的时间段(从当前时间所在的时间段开始)
纵坐标是当前时间段的累加值
分析:从当前时间,最主要的是设置 totalIndex
主要代码逻辑
// 从当前时间所在的时间段开始
long startTime = Long.parseLong(String.valueOf(datas.get(0).get(MINUTES)));
long totalIndex = (long) Math.floor((startTime - 1.0) / step) * step;;
TimeCycle timeCycle = TimeCycle.of(step, totalIndex);
// 初始值
cycleResults.add(new CycleResult(timeCycle.calculateStartTime(), 0L));
完整代码
public class TimeWindow {
/**
* 分钟数
*/
private static final String MINUTES = "minutes";
private static final String COUNT = "count";
private List<Map<String, Object>> datas = new ArrayList<>();
@Before
public void before() {
// 有序数据
Map<String, Object> map = new HashMap<String, Object>() {{
put(MINUTES, "50");
put(COUNT, 1);
}};
Map<String, Object> map2 = new HashMap<String, Object>() {{
put(MINUTES, "55");
put(COUNT, 2);
}};
Map<String, Object> map3 = new HashMap<String, Object>() {{
put(MINUTES, "62");
put(COUNT, 4);
}};
Map<String, Object> map4 = new HashMap<String, Object>() {{
put(MINUTES, "63");
put(COUNT, 8);
}};
datas.add(map);
datas.add(map2);
datas.add(map3);
datas.add(map4);
}
@Test
public void test() {
List<CycleResult> cycleResultList = collectCoordinate(10);
System.out.println(JSON.toJSONString(cycleResultList));
// [{"count":0,"minutesTime":"00:40"},{"count":1,"minutesTime":"00:50"},{"count":2,"minutesTime":"01:00"},{"count":12,"minutesTime":"01:10"}]
}
/**
* 坐标拾取
* @param step 步长
*/
public List<CycleResult> collectCoordinate(Integer step) {
List<CycleResult> cycleResults = new ArrayList<>();
// 从当前时间所在的时间段开始
long startTime = Long.parseLong(String.valueOf(datas.get(0).get(MINUTES)));
long totalIndex = (long) Math.floor((startTime - 1.0) / step) * step;;
TimeCycle timeCycle = TimeCycle.of(step, totalIndex);
// 初始值
cycleResults.add(new CycleResult(timeCycle.calculateStartTime(), 0L));
for (Map<String, Object> data : datas) {
Long minutes = data.get(MINUTES) == null ? 0L : Long.parseLong(String.valueOf(data.get(MINUTES)));
if (timeCycle.getTotalIndex() < minutes) {
while (timeCycle.getTotalIndex() < minutes) {
// 时间段内无值时补0
dealData(timeCycle, 0L, cycleResults);
}
}
if (timeCycle.getTotalIndex().equals(minutes)) {
long count = data.get(COUNT) == null ? 0L : Long.parseLong(String.valueOf(data.get(COUNT)));
dealData(timeCycle, count, cycleResults);
}
}
// 补全最后一批数据
if (timeCycle.hasData()) {
CycleResult cycleResult = new CycleResult();
cycleResult.setMinutesTime(timeCycle.calculateTime());
cycleResult.setCount(timeCycle.getCycleCount());
cycleResults.add(cycleResult);
timeCycle.reset();
}
return cycleResults;
}
private void dealData(TimeCycle timeCycle, Long count, List<CycleResult> cycleResults) {
if (cycleResults == null) {
cycleResults = new ArrayList<>();
}
// 累加
timeCycle.addCycleCount(count);
// 若达到了步长值(cycleIndex == step),就记录一个 TimeCycle
if (timeCycle.needRecord()) {
CycleResult result = new CycleResult();
result.setMinutesTime(timeCycle.calculateTime());
result.setCount(timeCycle.getCycleCount());
cycleResults.add(result);
timeCycle.reset();
} else {
timeCycle.increaseCycleIndex();
timeCycle.increaseTotalIndex();
}
}
}
@Data
public class TimeCycle {
private Integer step;
/**
* 步长周期里的索引
*/
private Integer cycleIndex = 0;
private Long cycleCount = 0L;
/**
* 从0开始自增,一直与值做比较,直到最一个数
*/
private Long totalIndex = 0L;
private Calendar calendar;
/**
* 工厂方法
* @param step 步长
*/
public static TimeCycle of(Integer step) {
TimeCycle timeCycle = new TimeCycle();
timeCycle.setStep(step);
return timeCycle;
}
public static TimeCycle of(Integer step, Long totalIndex) {
TimeCycle timeCycle = new TimeCycle();
timeCycle.setStep(step);
timeCycle.setTotalIndex(totalIndex);
return timeCycle;
}
public void increaseCycleIndex() {
this.cycleIndex++;
}
public void increaseTotalIndex() {
this.totalIndex++;
}
public void addCycleCount(Long currentCount) {
if (currentCount == null) {
return;
}
this.cycleCount += currentCount;
}
public Calendar getCalendar() {
if (this.calendar == null) {
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
this.calendar = calendar;
}
return this.calendar;
}
/**
* 初始值
* 时间格式 00:00
*/
public String calculateStartTime() {
return calculateTime(this.getRightStep());
}
/**
* Calendar不重置,每次增加一个步长
*/
public String calculateTime() {
return calculateTime(this.getStep());
}
private String calculateTime(Integer step) {
Calendar calendar = this.getCalendar();
calendar.add(Calendar.MINUTE, step);
return padStart(calendar.get(Calendar.HOUR_OF_DAY)) + ":" + padStart(calendar.get(Calendar.MINUTE));
}
private String padStart(int time) {
// 24小时制是两位数,不够左边补0
if (time < 10) {
return "0" + time;
}
return String.valueOf(time);
}
/**
* Math.ceil() 向上取整
* Math.floor() 向下取整
* Math.round() 四舍五入取整
*/
private Integer getLeftStep() {
return (int) Math.floor(totalIndex / (step * 1.0D)) * step;
}
private Integer getRightStep() {
return (int) Math.ceil(totalIndex / (step * 1.0D)) * step;
}
public boolean needRecord() {
return Objects.equals(this.getCycleIndex(), this.getStep());
}
public boolean hasData() {
return !(this.getCycleIndex() == 0 && this.getCycleCount() == 0L);
}
public void reset() {
this.setCycleIndex(0);
this.setCycleCount(0L);
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CycleResult {
private String minutesTime;
private Long count;
}