基于自关联BOOKS表与Java类,如何构建层级化图书展示列表?
实现扁平图书列表到层级系列结构的高效方案
核心思路
利用哈希表快速定位父节点,先将所有图书按ID建立索引映射,再通过遍历将子节点挂载到对应父节点的volumes列表中,最后筛选出顶级节点即可。由于层级最多4级,无论迭代还是递归都能高效完成,无需复杂逻辑。
具体步骤
- 构建图书索引映射:把扁平列表里的所有
Book对象存入HashMap<String, Book>,键为BOOK_ID,实现O(1)时间复杂度的父节点查找,避免重复遍历列表。 - 转换并挂载子节点:
- 遍历每本图书,若其
SERIES_ID不为空,从映射中取出对应的父节点。 - 若父节点是普通
Book,将其转换为Series实例(继承关系支持属性复制转换),并初始化volumes列表。 - 将当前图书添加到父节点的
volumes列表,同时按SERIES_VOL_NUM排序,保证卷号顺序符合展示需求。
- 遍历每本图书,若其
- 收集顶级节点:筛选出
SERIES_ID为空的节点,这些就是层级结构的顶级项(包括独立图书和顶级系列)。
代码实现示例
import java.util.*; import java.util.stream.Collectors; public class BookHierarchyBuilder { public static List<Book> buildHierarchy(List<Book> flatBooks) { // 构建ID到Book的映射,快速查找父节点 Map<String, Book> bookMap = flatBooks.stream() .collect(Collectors.toMap(Book::getBookId, book -> book)); // 遍历处理所有子节点挂载 for (Book book : flatBooks) { String seriesId = book.getSeriesId(); if (seriesId != null && bookMap.containsKey(seriesId)) { Book parentBook = bookMap.get(seriesId); Series parentSeries; // 父节点为普通Book时,转换为Series类型 if (!(parentBook instanceof Series)) { parentSeries = convertBookToSeries(parentBook); bookMap.put(seriesId, parentSeries); // 更新映射中的父节点实例 } else { parentSeries = (Series) parentBook; } // 添加当前图书并按卷号排序 List<Book> volumes = Optional.ofNullable(parentSeries.getVolumes()) .orElseGet(ArrayList::new); volumes.add(book); volumes.sort(Comparator.comparing(Book::getSeriesVolNum, Comparator.nullsLast(Integer::compare))); parentSeries.setVolumes(volumes); } } // 收集所有顶级节点(无父节点的图书/系列) return flatBooks.stream() .filter(book -> book.getSeriesId() == null) .map(book -> bookMap.get(book.getBookId())) .collect(Collectors.toList()); } // 将普通Book转换为Series,复制所有属性 private static Series convertBookToSeries(Book book) { Series series = new Series(); series.setBookId(book.getBookId()); series.setTitle(book.getTitle()); series.setUrl(book.getUrl()); series.setPublishedOn(book.getPublishedOn()); series.setIsbn(book.getIsbn()); series.setAnnotation(book.getAnnotation()); series.setCommentary(book.getCommentary()); series.setRating(book.getRating()); series.setSeriesId(book.getSeriesId()); series.setSeriesVolNum(book.getSeriesVolNum()); series.setVolumes(new ArrayList<>()); return series; } }
额外说明
- 时间复杂度:整体为O(n log n),主要来自卷号排序操作,其余步骤均为O(n),处理图书列表效率足够。
- 层级适配:即使嵌套到4级,上述逻辑会自动处理父节点的转换与挂载,无需额外的层级判断。
- 空值兼容:排序逻辑兼容
SERIES_VOL_NUM为空的情况,将这类条目放在系列末尾。
内容的提问来源于stack exchange,提问作者Gary Greenberg
相关产品推荐
相关产品推荐

