文章插图
文章插图
唐宋八大家之一欧阳修在《卖油翁》中写道:
翁取一葫芦置于地,以钱覆其口,徐以杓酌油沥之,自钱孔入,而钱不湿 。因曰:“我亦无他,唯手熟尔 。”
【判断数组是不是空 js如何判断数组是否为空】编写代码的”老司机”也是如此,”老司机”之所以被称为”老司机”,原因也是”无他,唯手熟尔” 。编码过程中踩过的坑多了,获得的编码经验也就多了,总结的编码技巧也就更多了 。总结的编码技巧多了,凡事又能够举一反三,编码的速度自然就上来了 。笔者从数据结构的角度,整理了一些 Java 编程技巧,以供大家学习参考 。
使用HashSet判断主键是否存在
HashSet 实现 Set 接口,由哈希表(实际上是 HashMap )实现,但不保证 set 的迭代顺序,并允许使用 元素 。HashSet 的时间复杂度跟 HashMap 一致,如果没有哈希冲突则时间复杂度为 O(1) ,如果存在哈希冲突则时间复杂度不超过 O(n)。所以,在日常编码中,可以使用 HashSet 判断主键是否存在 。
案例:给定一个字符串(不一定全为字母),请返回第一个重复出现的字符 。
/** 查找第一个重复字符 */public static char findFirstRepeatedChar(String string) {// 检查空字符串if (Objects.is(string) || string.isEmpty) {return ;}// 查找重复字符char charArray = string.toCharArray;Set charSet = new HashSet<>(charArray.length);for (char ch : charArray) {if (charSet.contains(ch)) {return ch;}charSet.add(ch);}// 默认返回为空return ;}
其中,由于 Set 的 add 函数有个特性——如果添加的元素已经再集合中存在,则返回 false。可以简化代码为:if (!charSet.add(ch)) {return ch;}
使用HashMap存取键值映射关系简单来说,HashMap 由数组和链表组成的,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的 。如果定位到的数组位置不含链表,那么查找、添加等操作很快,仅需一次寻址即可,其时间复杂度为 O(1) ;如果定位到的数组包含链表,对于添加操作,其时间复杂度为 O(n) ——首先遍历链表,存在即覆盖,不存在则新增;对于查找操作来讲,仍需要遍历链表,然后通过key对象的 equals 方法逐一对比查找 。从性能上考虑, HashMap 中的链表出现越少,即哈希冲突越少,性能也就越好 。所以,在日常编码中,可以使用 HashMap 存取键值映射关系 。
案例:给定菜单记录列表,每条菜单记录中包含父菜单标识(根菜单的父菜单标识为 ),构建出整个菜单树 。
/** 菜单DO类 [email protected]@[email protected] static class MenuDO {/** 菜单标识 */private Long id;/** 菜单父标识 */private Long parentId;/** 菜单名称 */private String name;/** 菜单链接 */private String url;}/** 菜单VO类 [email protected]@[email protected] 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.is(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.non(parentId)) {// 构建父菜单对象MenuVO parentMenu = menuMap.get(parentId);if (Objects.is(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 存储线程专有对象ThreadLocal 提供了线程专有对象,可以在整个线程生命周期中随时取用,极大地方便了一些逻辑的实现 。
常见的 ThreadLocal 用法主要有两种:
1、保存线程上下文对象,避免多层级参数传递;
2、保存非线程安全对象,避免多线程并发调用 。
保存线程上下文对象,避免多层级参数传递
这里,以 PageHelper 插件的源代码中的分页参数设置与使用为例说明 。
设置分页参数代码:
/** 分页方法类 */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 != && 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 [email protected] String getPageSql(M
- 米糕用的米是什么米是不是糯米吗,米糕用什么米做最好吃
- 发芽的大蒜是不是有毒,蒜发苦了还能吃吗
- 怎样判断女人出轨了,怎么判断女生和其他男生有没有关系?
- 夏天高层楼顶是不是特殊热
- 番石榴怎么判断熟了
- 怎么看出女生睡过多少人 判断女朋友和多少人睡过
- 怎样判断女人出轨了:如何判断妻子是否有外遇?
- 淘宝流量推广软件是真的吗 淘宝推广是不是真的
- 看看你的孩子是不是这样 看看你的他是不是这样
- matlab一维数组索引 matlab二维矩阵索引