Echarts中图表类型大部分所需要的数据都是一个横坐标对应一至多个纵坐标,有些饼状图类的数据有些特殊,但是绝大部分的图表所需要的数据结构还是一致的,因此这里设计一个视图类用来快速创建像该种图表所需要的数据
思路:由上面的一一对应这种关系,很容易想到使用一个Map结构来保存数据,对于绝大多数的图表结构,其横坐标都是可排序的, 因此我们可以聚合 TreeMap 和 Comparator 这两个对象来完成
public class EChartsVO<X, Y> {
@JsonValue
private TreeMap<X, Y> data; // 使用TreeMap存储数据
private Comparator<? super X> cmp; // 比较器
/**
* @param cmp X比较器
* 传入X的比较器,并初始化TreeMap;
*/
public EChartsVO(Comparator<? super X> cmp) {
this.cmp = cmp;
this.data = new TreeMap<>(cmp);
}
/**
* 默认构造器会将X当作Comparable接口的子类,
* @throws UnsupportedOperationException 如果横坐标没有实现Comparable将会构造失败抛出 UnsupportedOperationException
*/
public EChartsVO() {
this.cmp = new Comparator<X>() {
@Override
public int compare(X o1, X o2) {
if (o1 instanceof Comparable && o2 instanceof Comparable) {
return ((Comparable<? super X>) o1).compareTo(o2);
} else {
throw new UnsupportedOperationException(String.format("Cannot compare instances of type %s", o1.getClass()));
}
}
};
this.data = new TreeMap<>(this.cmp);
}
/**
* 获取的是data的不可变Map
*/
public Map<X, Y> getData(){return Collections.unmodifiableMap(data);}
/**
* 反转data中的数据
*/
public void reverse() {
TreeMap<X, Y> map = new TreeMap<>(cmp = this.cmp.reversed());
map.putAll(this.data);
this.data = map;
}
public void clear(){
this.data.clear();
}
}
类的框架和基本构造就写好了, 接下来是提供方法用来保存数据
首先很容易想到保存一组键值对:
public void putData(X x, Y y) {
Objects.requireNonNull(x, "element x can not be null");
this.data.put(x, y);
}
也可以传入一个现有的Map集合调用Map.putAll()方法快速创建
public void putData(@NotNull Map<? extends X, ? extends Y> arg){
Set<? extends X> keys = arg.keySet();
if(keys.stream().anyMatch(Objects::isNull)){
throw new IllegalArgumentException("element x can not be null");
}
this.data.putAll(arg);
}
还有一种方式,需要让外界传入数据集合,以及两个Function对象,一个用来将X轴映射出来, 另一个将Y轴映射出来
public <T> void putData(@NotNull Collection<T> list,
@NotNull Function<? super T, ? extends X> XMapper,
@NotNull Function<? super T ,? extends Y> YMapper){
Objects.requireNonNull(list);
Objects.requireNonNull(XMapper);
Objects.requireNonNull(YMapper);
Iterator<T> iterator = list.iterator();
while(iterator.hasNext()){
T next = iterator.next();
X x;
if((x = XMapper.apply(next)) == null){
throw new IllegalArgumentException("element x can not be null");
}
this.data.put(x, YMapper.apply(next));
}
}
实际使用:
// 适合 一个X对应一个Y
EChartsVO<String, Integer> vo = new EChartsVO<>();
if(!CollectionUtils.isEmpty(dtos)){
vo.putData(dtos, WorkerRegionCountDTO::getProvince, WorkerRegionCountDTO::getWorkerCount);
}
//适合一个X对应多个Y
public class InspectAndAttendanceVO {
public InspectAndAttendanceVO() {
this.data = new EChartsVO<>();
}
private Long workerId;
private String workerName;
private String position;
private EChartsVO<LocalDate, AttendanceVO> data;
public void addData(String k, List<InspectAndAttendanceDTO> v) {
String[] baseInfo = k.split("-");
if (baseInfo.length != 3) {
throw new IllegalArgumentException("data is not correct :" + k);
}
int idx = 0;
this.workerId = Long.valueOf(baseInfo[idx++]);
this.workerName = baseInfo[idx++];
this.position = baseInfo[idx++];
Map<LocalDate, AttendanceVO> map = v.stream().collect(Collectors.toMap(InspectAndAttendanceDTO::getDate, AttendanceVO::new));
this.data.putData(map);
}
@Data
private static class AttendanceVO {
private Integer isPresent;
private Integer isInspect;
public AttendanceVO(InspectAndAttendanceDTO dto) {
this.isPresent = dto.getIsPresent();
this.isInspect = dto.getIsInspect();
}
}
}