Java 编程技巧之数据结构
The following article is from 阿里巴巴中间件 Author 常意
点击上方"IT牧场",选择"设为星标"技术干货每日送达!
Photo @markusspiske
文 | 常意
导读
翁取一葫芦置于地,以钱覆其口,徐以杓酌油沥之,自钱孔入,而钱不湿。因曰:“我亦无他,唯手熟尔。”
使用HashSet判断主键是否存在
public static char findFirstRepeatedChar(String string) {
// 检查空字符串
if (Objects.isNull(string) || string.isEmpty()) {
return null;
}
// 查找重复字符
char[] charArray = string.toCharArray();
Set charSet = new HashSet<>(charArray.length);
for (char ch : charArray) {
if (charSet.contains(ch)) {
return ch;
}
charSet.add(ch);
}
// 默认返回为空
return null;
}
return ch;
}
使用HashMap存取键值映射关系
@Setter
@Getter
@ToString
public static class MenuDO {
/** 菜单标识 */
private Long id;
/** 菜单父标识 */
private Long parentId;
/** 菜单名称 */
private String name;
/** 菜单链接 */
private String url;
}
/** 菜单VO类 */
@Setter
@Getter
@ToString
public static class MenuVO {
/** 菜单标识 */
private Long id;
/** 菜单名称 */
private String name;
/** 菜单链接 */
private String url;
/** 子菜单列表 */
private List<MenuVO> childList;
}
/** 构建菜单树函数 */
public static List<MenuVO> buildMenuTree(List<MenuDO> menuList) {
// 检查列表为空
if (CollectionUtils.isEmpty(menuList)) {
return Collections.emptyList();
}
// 依次处理菜单
int menuSize = menuList.size();
List<MenuVO> rootList = new ArrayList<>(menuSize);
Map<Long, MenuVO> menuMap = new HashMap<>(menuSize);
for (MenuDO menuDO : menuList) {
// 赋值菜单对象
Long menuId = menuDO.getId();
MenuVO menu = menuMap.get(menuId);
if (Objects.isNull(menu)) {
menu = new MenuVO();
menu.setChildList(new ArrayList<>());
menuMap.put(menuId, menu);
}
menu.setId(menuDO.getId());
menu.setName(menuDO.getName());
menu.setUrl(menuDO.getUrl());
// 根据父标识处理
Long parentId = menuDO.getParentId();
if (Objects.nonNull(parentId)) {
// 构建父菜单对象
MenuVO parentMenu = menuMap.get(parentId);
if (Objects.isNull(parentMenu)) {
parentMenu = new MenuVO();
parentMenu.setId(parentId);
parentMenu.setChildList(new ArrayList<>());
menuMap.put(parentId, parentMenu);
}
// 添加子菜单对象
parentMenu.getChildList().add(menu);
} else {
// 添加根菜单对象
rootList.add(menu);
}
}
// 返回根菜单列表
return rootList;
}
使用 ThreadLocal 存储线程专有对象
保存线程上下文对象,避免多层级参数传递
public abstract class PageMethod {
/** 本地分页 */
protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal<Page>();
/** 设置分页参数 */
protected static void setLocalPage(Page page) {
LOCAL_PAGE.set(page);
}
/** 获取分页参数 */
public static <T> Page<T> getLocalPage() {
return LOCAL_PAGE.get();
}
/** 开始分页 */
public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {
Page<E> page = new Page<E>(pageNum, pageSize, count);
page.setReasonable(reasonable);
page.setPageSizeZero(pageSizeZero);
Page<E> oldPage = getLocalPage();
if (oldPage != null && oldPage.isOrderByOnly()) {
page.setOrderBy(oldPage.getOrderBy());
}
setLocalPage(page);
return page;
}
}
public abstract class AbstractHelperDialect extends AbstractDialect implements Constant {
/** 获取本地分页 */
public <T> Page<T> getLocalPage() {
return PageHelper.getLocalPage();
}
/** 获取分页SQL */
@Override
public String getPageSql(MappedStatement ms, BoundSql boundSql, Object parameterObject, RowBounds rowBounds, CacheKey pageKey) {
String sql = boundSql.getSql();
Page page = getLocalPage();
String orderBy = page.getOrderBy();
if (StringUtil.isNotEmpty(orderBy)) {
pageKey.update(orderBy);
sql = OrderByParser.converToOrderBySql(sql, orderBy);
}
if (page.isOrderByOnly()) {
return sql;
}
return getPageSql(sql, page, pageKey);
}
...
}
public PageInfo<UserDO> queryUser(UserQuery userQuery, int pageNum, int pageSize) {
PageHelper.startPage(pageNum, pageSize);
List<UserDO> userList = userDAO.queryUser(userQuery);
PageInfo<UserDO> pageInfo = new PageInfo<>(userList);
return pageInfo;
}
保存非线程安全对象,避免多线程并发调用
private static final String DATE_PATTERN = "yyyy-MM-dd";
/** 格式化日期函数 */
public static String formatDate(Date date) {
return new SimpleDateFormat(DATE_PATTERN).format(date);
}
private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd");
/** 格式化日期函数 */
public static String formatDate(Date date) {
return DATE_FORMAT.format(date);
}
private static final ThreadLocal<DateFormat> LOCAL_DATE_FORMAT = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
/** 格式化日期函数 */
public static String formatDate(Date date) {
return LOCAL_DATE_FORMAT.get().format(date);
}
使用 Pair 实现成对结果的返回
定义模型类实现成对结果的返回
@Setter
@Getter
@ToString
@AllArgsConstructor
public static class PointAndDistance {
/** 点 */
private Point point;
/** 距离 */
private Double distance;
}
/** 获取最近点和距离 */
public static PointAndDistance getNearestPointAndDistance(Point point, Point[] points) {
// 检查点数组为空
if (ArrayUtils.isEmpty(points)) {
return null;
}
// 获取最近点和距离
Point nearestPoint = points[0];
double nearestDistance = getDistance(point, points[0]);
for (int i = 1; i < points.length; i++) {
double distance = getDistance(point, point[i]);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestPoint = point[i];
}
}
// 返回最近点和距离
return new PointAndDistance(nearestPoint, nearestDistance);
}
函数使用案例:
Point point = ...;Point[] points = ...;
PointAndDistance pointAndDistance = getNearestPointAndDistance(point, points);
if (Objects.nonNull(pointAndDistance)) {
Point point = pointAndDistance.getPoint();
Double distance = pointAndDistance.getDistance();
...
}
使用 Pair 类实现成对结果的返回
public static Pair<Point, Double> getNearestPointAndDistance(Point point, Point[] points) {
// 检查点数组为空
if (ArrayUtils.isEmpty(points)) {
return null;
}
// 获取最近点和距离
Point nearestPoint = points[0];
double nearestDistance = getDistance(point, points[0]);
for (int i = 1; i < points.length; i++) {
double distance = getDistance(point, point[i]);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestPoint = point[i];
}
}
// 返回最近点和距离
return Pair.of(nearestPoint, nearestDistance);
}
Point[] points = ...;
Pair<Point, Double> pair = getNearestPointAndDistance(point, points);
if (Objects.nonNull(pair)) {
Point point = pair.getLeft();
Double distance = pair.getRight();
...
}
定义 Enum 类实现取值和描述
用 class 关键字实现的枚举类型
public final class OrderStatus {
/** 属性相关 */
/** 状态取值 */
private final int value;
/** 状态描述 */
private final String description;
/** 常量相关 */
/** 已创建(1) */
public static final OrderStatus CREATED = new OrderStatus(1, "已创建");
/** 进行中(2) */
public static final OrderStatus PROCESSING = new OrderStatus(2, "进行中");
/** 已完成(3) */
public static final OrderStatus FINISHED = new OrderStatus(3, "已完成");
/** 构造函数 */
private OrderStatus(int value, String description) {
this.value = value;
this.description = description;
}
/** 获取状态取值 */
public int getValue() {
return value;
}
/** 获取状态描述 */
public String getDescription() {
return description;
}
}
用 enum 关键字实现的枚举类型
public enum OrderStatus {
/** 常量相关 */
/** 已创建(1) */
CREATED(1, "已创建"),
/** 进行中(2) */
PROCESSING(2, "进行中"),
/** 已完成(3) */
FINISHED(3, "已完成");
/** 属性相关 */
/** 状态取值 */
private final int value;
/** 状态描述 */
private final String description;
/** 构造函数 */
private OrderStatus(int value, String description) {
this.value = value;
this.description = description;
}
/** 获取状态取值 */
public int getValue() {
return value;
}
/** 获取状态描述 */
public String getDescription() {
return description;
}
}
定义 Holder 类实现参数的输出
@Getter
@Setter
@ToString
public class LongHolder {
/** 长整型取值 */
private long value;
/** 构造函数 */
public LongHolder() {}
/** 构造函数 */
public LongHolder(long value) {
this.value = value;
}
}
/** 静态常量 */
/** 页面数量 */
private static final int PAGE_COUNT = 100;
/** 最大数量 */
private static final int MAX_COUNT = 1000;
/** 处理过期订单 */
public void handleExpiredOrder() {
LongHolder minIdHolder = new LongHolder(0L);
for (int pageIndex = 0; pageIndex < PAGE_COUNT; pageIndex++) {
if (!handleExpiredOrder(pageIndex, minIdHolder)) {
break;
}
}
}
/** 处理过期订单 */
private boolean handleExpiredOrder(int pageIndex, LongHolder minIdHolder) {
// 获取最小标识
Long minId = minIdHolder.getValue();
// 查询过期订单(按id从小到大排序)
List<OrderDO> orderList = orderDAO.queryExpired(minId, MAX_COUNT);
if (CollectionUtils.isEmpty(taskTagList)) {
return false;
}
// 设置最小标识
int orderSize = orderList.size();
minId = orderList.get(orderSize - 1).getId();
minIdHolder.setValue(minId);
// 依次处理订单
for (OrderDO order : orderList) {
...
}
// 判断还有订单
return orderSize >= PAGE_SIZE;
}
定义 Union 类实现数据体的共存
使用函数方式实现 Union
@ToString
public class CustomerMessage {
/** 属性相关 */
/** 消息类型 */
private String msgType;
/** 目标用户 */
private String toUser;
/** 共用体相关 */
/** 新闻内容 */
private News news;
...
/** 常量相关 */
/** 新闻消息 */
public static final String MSG_TYPE_NEWS = "news";
...
/** 构造函数 */
public CustomerMessage() {}
/** 构造函数 */
public CustomerMessage(String toUser) {
this.toUser = toUser;
}
/** 构造函数 */
public CustomerMessage(String toUser, News news) {
this.toUser = toUser;
this.msgType = MSG_TYPE_NEWS;
this.news = news;
}
/** 清除消息内容 */
private void removeMsgContent() {
// 检查消息类型
if (Objects.isNull(msgType)) {
return;
}
// 清除消息内容
if (MSG_TYPE_NEWS.equals(msgType)) {
news = null;
} else if (...) {
...
}
msgType = null;
}
/** 检查消息类型 */
private void checkMsgType(String msgType) {
// 检查消息类型
if (Objects.isNull(msgType)) {
throw new IllegalArgumentException("消息类型为空");
}
// 比较消息类型
if (!Objects.equals(msgType, this.msgType)) {
throw new IllegalArgumentException("消息类型不匹配");
}
}
/** 设置消息类型函数 */
public void setMsgType(String msgType) {
// 清除消息内容
removeMsgContent();
// 检查消息类型
if (Objects.isNull(msgType)) {
throw new IllegalArgumentException("消息类型为空");
}
// 赋值消息内容
this.msgType = msgType;
if (MSG_TYPE_NEWS.equals(msgType)) {
news = new News();
} else if (...) {
...
} else {
throw new IllegalArgumentException("消息类型不支持");
}
}
/** 获取消息类型 */
public String getMsgType() {
// 检查消息类型
if (Objects.isNull(msgType)) {
throw new IllegalArgumentException("消息类型无效");
}
// 返回消息类型
return this.msgType;
}
/** 设置新闻 */
public void setNews(News news) {
// 清除消息内容
removeMsgContent();
// 赋值消息内容
this.msgType = MSG_TYPE_NEWS;
this.news = news;
}
/** 获取新闻 */
public News getNews() {
// 检查消息类型
checkMsgType(MSG_TYPE_NEWS);
// 返回消息内容
return this.news;
}
...
}
String toUser = ...;
List<Article> articleList = ...;
News news = new News(articleList);
CustomerMessage customerMessage = new CustomerMessage(toUser, news);
wechatApi.sendCustomerMessage(accessToken, customerMessage);
优点:更贴近 C/C++ 语言的联合体(union); 缺点:实现逻辑较为复杂,参数类型验证较多。
使用继承方式实现 Union
@Getter
@Setter
@ToString
public abstract class CustomerMessage {
/** 属性相关 */
/** 消息类型 */
private String msgType;
/** 目标用户 */
private String toUser;
/** 常量相关 */
/** 新闻消息 */
public static final String MSG_TYPE_NEWS = "news";
...
/** 构造函数 */
public CustomerMessage(String msgType) {
this.msgType = msgType;
}
/** 构造函数 */
public CustomerMessage(String msgType, String toUser) {
this.msgType = msgType;
this.toUser = toUser;
}
}
/** 新闻客户消息类 */
@Getter
@Setter
@ToString(callSuper = true)
public class NewsCustomerMessage extends CustomerMessage {
/** 属性相关 */
/** 新闻内容 */
private News news;
/** 构造函数 */
public NewsCustomerMessage() {
super(MSG_TYPE_NEWS);
}
/** 构造函数 */
public NewsCustomerMessage(String toUser, News news) {
super(MSG_TYPE_NEWS, toUser);
this.news = news;
}
}
String toUser = ...;
List<Article> articleList = ...;
News news = new News(articleList);
CustomerMessage customerMessage = new NewsCustomerMessage(toUser, news);
wechatApi.sendCustomerMessage(accessToken, customerMessage);
优点:使用虚基类和子类进行拆分,各个子类对象的概念明确; 缺点:与 C/C++ 语言的联合体(union)差别大,但是功能上大体一致。
使用泛型屏蔽类型的差异性
泛型类
@Getter
@Setter
@ToString
public class GenericHolder<T> {
/** 通用取值 */
private T value;
/** 构造函数 */
public GenericHolder() {}
/** 构造函数 */
public GenericHolder(T value) {
this.value = value;
}
}
泛型接口
public interface DataProvider<T> {
/** 获取数据函数 */
public T getData();
}
泛型方法
public static <T> T shallowCopy(Object source, Class<T> clazz) throws BeansException {
// 判断源对象
if (Objects.isNull(source)) {
return null;
}
// 新建目标对象
T target;
try {
target = clazz.newInstance();
} catch (Exception e) {
throw new BeansException("新建类实例异常", e);
}
// 拷贝对象属性
BeanUtils.copyProperties(source, target);
// 返回目标对象
return target;
}
泛型通配符
泛型通配符一般是使用"?"代替具体的类型实参,可以把"?"看成所有类型的父类。当具体类型不确定的时候,可以使用泛型通配符 "?";当不需要使用类型的具体功能,只使用Object类中的功能时,可以使用泛型通配符 "?"。
public static void printValue(GenericHolder<?> holder) {
System.out.println(holder.getValue());
}
/** 主函数 */
public static void main(String[] args) {
printValue(new GenericHolder<>(12345));
printValue(new GenericHolder<>("abcde"));
}
public static <T> void printValue(GenericHolder<T> holder) {
System.out.println(holder.getValue());
}
泛型上下界
@Getter
@Setter
@ToString
public class NumberHolder<T extends Number> {
/** 通用取值 */
private T value;
/** 构造函数 */
public NumberHolder() {}
/** 构造函数 */
public NumberHolder(T value) {
this.value = value;
}
}
/** 打印取值函数 */
public static <T extends Number> void printValue(GenericHolder<T> holder) {
System.out.println(holder.getValue());
}
后记
陈昌毅,花名常意,高德地图技术专家,2018年加入阿里巴巴,一直从事地图数据采集的相关工作。
干货分享
最近将个人学习笔记整理成册,使用PDF分享。关注我,回复如下代码,即可获得百度盘地址,无套路领取!
•001:《Java并发与高并发解决方案》学习笔记;•002:《深入JVM内核——原理、诊断与优化》学习笔记;•003:《Java面试宝典》•004:《Docker开源书》•005:《Kubernetes开源书》•006:《DDD速成(领域驱动设计速成)》•007:全部•008:加技术讨论群
近期热文
•JVM性能调优监控工具jps、jstack、jmap、jhat、jstat、hprof使用详解•如何减少长时间的 GC 停顿?•代码调试最佳实践•代码编排架构三部曲简述•超全面的 MySQL优化 面试解析•让人头大的各种锁,从这里让你思绪清晰
想知道更多?长按/扫码关注我吧↓↓↓