集合排序和流排序
import lombok.extern.slf4j.Slf4j; import java.util.Arrays; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.stream.Collectors; /** * HBase工具类提供数据解析和多列排序功能 * * author * date * description SortUtils - 提供从HBase数据字符串中按索引提取元素以及多列混合排序的工具方法 */ Slf4j public class SortUtils { /** * 根据索引从 HBase 原始数据字符串中获取对应元素 * param rowData 原始整行数据例如: 20260211||00T1...||637251637... * param index 需要的列索引从0开始 * return 对应列的值若索引越界或数据为空则返回 null */ public static String getByIndex(String rowData, int index) { if (rowData null || rowData.isEmpty() || index 0) { return null; } // 01. 去除首位可能存在的双引号 String cleanData rowData.replace(\, ); // 02. 按照 || 分割。使用 -01 参数防止丢弃末尾空的字符串 String[] columns cleanData.split(\\|\\\\|, -01); // 03. 边界检查防止数组越界 if (index columns.length) { return null; } return columns[index]; } public static void main(String[] args) { ListString pureList Arrays.asList( 20260211||00T108220000||637204815936281||402817359065423||03||02||48215370||22749815||001020260822|, 20260209||00T206221103||152738906428477||716054293813065||04||03||13908652||60519247||001020260822|, 20260214||00T308220000||904671253804619||285603714092536||01||05||75310924||8342670||001020260822|, 20260210||00T108220000||578019342675208||693450127806432||06||01||20573918||94165703||001020260822|, 20260212||00T206221103||310846597123780||847219635012894||02||04||68731509||15384062||001020260822|, 20260213||00T308220000||729065381940265||580124769203816||03||01||95261784||36028715||001020260822|, 20260208||00T108220000||461597208368513||073851492605348||05||02||12480736||59762048||001020260822|, 20260215||00T108220000||893560724816032||326978041539607||02||05||53048291||81635927||001020260822|, 20260216||00T308220000||204571983650137||918036547291084||01||03||84729150||39206148||001020260822|, 20260218||00T108220000||563129804782228||147095328604371||04||06||21894637||78563012||001020260822|, 20260217||00T206221103||780314259680431||402615739801526||02||01||90631752||53042879||001020260822|, 20260220||00T108220000||319684057123756||825074613982041||05||04||13759206||67480239||001020260822|, 20260219||00T308220000||095428617305926||634891720536840||03||02||46903581||05382961||001020260822|, 20260222||00T108220000||641802395746102||207568914329675||06||01||75823690||12607934||001020260822|, 20260221||00T108220000||827604931578240||490352187064516||01||05||39265708||85194026||001020260822|, 20260224||00T508220000||105729638402675||768301529470318||04||02||58470163||24937605||001020260822|, 20260223||00T108220000||476015829340587||031946825710963||02||06||91360487||60571238||001020260822|, 20260226||00T408220000||358297061485019||614073895630287||05||03||02749518||40682175||001020260822|, 20260225||00T308220000||709436128057314||582901463725630||03||01||56023891||73016942||001020260822|, 20260227||00T108220000||234890782228604||857410392605173||06||05||89650123||19473056||001020260822|, 20260228||00T408220000||461593028782204||391028564702381||01||04||73591824||50612973||001020260822|, 20260301||00T206221103||915836204759382||627403158609425||05||02||26473059||81590634||001020260822|, 20260302||00T108220000||380451796208317||509372814652018||03||06||90274185||46283901||001020260822|, 20260303||00T508220000||726594083165924||813047295683740||02||01||58461927||37195082||001020260822|, 20260304||00T108220000||149028637502918||405918273615094||06||03||83756219||06253914||001020260822|, 20260305||00T308220000||593618204857320||928047516302815||01||05||41802537||72938405||001020260822|, 20260306||00T108220000||865293041726084||378220928405137||04||02||60281935||95140762||001020260822|, 20260307||00T408220000||207491583609215||584630192750368||02||06||53708146||19482735||001020260822|, 20260308||00T108220000||738416205973048||401529687352190||05||01||90263714||56039428||001020260822|, 20260309||00T608220000||419285076314529||862530914708356||03||04||15847206||73061829||001020260822|, 20260310||00T308220000||605437192860374||293847056191042||06||05||39481625||05827491||001020260822|, 20260311||00T108220000||082594617350281||751930482627035||01||02||46273051||80371924||001020260822|, 20260312||00T508220000||730492882210528||506283947102584||02||05||39462817||71038526||001020260822|, 20260313||00T108220000||318064759283516||847210693501728||05||01||62740139||40281596||001020260822|, 20260314||00T206221103||964172058460293||235719840615382||01||03||81029546||57396104||001020260822|, 20260315||00T108220000||405839261713059||793058624109418||04||06||51739204||64028195||001020260822|, 20260318||00T408220000||271506394825017||468392015726093||02||02||39584016||80271594||001020260822|, 20260319||00T108220000||824917063501284||150462938504716||06||01||61820539||39461825||001020260822|, 20260320||00T408220000||573840192640385||302795814602359||01||05||42615073||94158603||001020260822|, 20260321||00T108220000||906157384529160||549310268405137||03||04||73594218||06283749||001020260822|, 20260322||00T308220000||152839475603918||726409381502716||05||02||84016275||31750468||001020260822|, 20260325||00T108220000||493618205713049||603827159406258||02||06||56041938||90271546||001020260822| ); // 方法一集合排序Collections.sort 自定义Comparator // 排序规则日期降序、列2降序、列3降序、列4升序、列5升序、列8降序 log.info( 集合排序Collections.sort); ListString sortedByCollections new ArrayList(pureList); Collections.sort(sortedByCollections, (row1, row2) - { int cmp; // 列0降序row2与row1比较实现降序效果 cmp getByIndex(row2, 0).compareTo(getByIndex(row1, 0)); if (cmp ! 0) { return cmp; } // 列2降序 cmp getByIndex(row2, 02).compareTo(getByIndex(row1, 02)); if (cmp ! 0) { return cmp; } // 列3降序 cmp getByIndex(row2, 03).compareTo(getByIndex(row1, 03)); if (cmp ! 0) { return cmp; } // 列4升序row1与row2比较实现升序效果 cmp getByIndex(row1, 04).compareTo(getByIndex(row2, 04)); if (cmp ! 0) { return cmp; } // 列5升序 cmp getByIndex(row1, 05).compareTo(getByIndex(row2, 05)); if (cmp ! 0) { return cmp; } // 列8降序 return getByIndex(row2, 8).compareTo(getByIndex(row1, 8)); }); sortedByCollections.forEach(row - log.info(Collections排序: {}, row)); /** * 方法一优缺点分析 * * 优点 * 1. 直观清晰通过row1和row2的参数交换方式直接表达升序或降序代码逻辑一目了然 * 2. 灵活性高可以在任意比较步骤中加入复杂的业务逻辑判断 * 3. 性能稳定不涉及额外的对象创建内存开销小 * 4. 精确控制每个比较步骤都可以精确控制升序/降序不容易出错 * * 缺点 * 1. 代码冗长每个比较步骤都需要if判断和return语句代码行数较多 * 2. 可读性较差参数交换方式row2, row1需要仔细阅读才能理解排序方向 * 3. 维护成本高如果需要修改排序规则需要修改多处代码容易遗漏 * 4. 缺乏复用性排序逻辑内联在lambda中难以复用和测试 */ // 方法二流排序Stream.sorted 链式Comparator log.info( 流排序Stream.sorted); ListString sortedByStream pureList.stream() .sorted(Comparator.comparing((String row) - getByIndex(row, 0)).reversed() .thenComparing(row - getByIndex(row, 02)).reversed() .thenComparing(row - getByIndex(row, 03)).reversed() .thenComparing(row - getByIndex(row, 04)) .thenComparing(row - getByIndex(row, 05)) .thenComparing(row - getByIndex(row, 8)).reversed()) .collect(Collectors.toList()); sortedByStream.forEach(row - log.info(Stream排序: {}, row)); /** * 方法二优缺点分析 * * 优点 * 1. 函数式风格使用链式调用代码简洁优雅符合现代Java编程风格 * 2. 声明式表达通过reversed()清晰表达升序/降序意图可读性强 * 3. 易于扩展可以轻松添加更多排序条件只需链式添加thenComparing * 4. 符合最佳实践充分利用Java 8 Stream API的特性 * 5. 适合管道处理可以与其他流操作filter、map等无缝集成 * * 缺点 * 1. 性能开销涉及Stream的创建和collect操作有额外的性能开销 * 2. 内存消耗Stream操作过程中可能产生中间对象内存占用相对较高 * 3. 调试困难流式操作的链式调用调试不便问题定位相对复杂 * 4. 学习曲线对不熟悉Stream API的开发者来说理解成本较高 * 5. 重复解析每次比较都调用getByIndex解析字符串可能存在性能优化空间 */ // 方法三集合排序Comparator.comparing reversed log.info( 集合排序Comparator.comparing reversed ); ListString sortedByComparator new ArrayList(pureList); sortedByComparator.sort(Comparator .comparing((String row) - getByIndex(row, 0)).reversed() .thenComparing(row - getByIndex(row, 02)).reversed() .thenComparing(row - getByIndex(row, 03)).reversed() .thenComparing(row - getByIndex(row, 04)) .thenComparing(row - getByIndex(row, 05)) .thenComparing(row - getByIndex(row, 8)).reversed()); sortedByComparator.forEach(row - log.info(Comparator排序: {}, row)); /** * 方法三优缺点分析 * * 优点 * 1. 性能最优直接在List上排序避免了Stream的创建和收集操作 * 2. 代码简洁链式调用比传统Comparator写法更简洁 * 3. 表达清晰通过reversed()方法明确表达排序方向易于理解 * 4. 内存高效不创建额外的中间集合内存使用效率高 * 5. 复用性强可以将Comparator单独提取出来在不同场景复用 * * 缺点 * 1. 类型冗余需要显式声明(String row)类型参数略显冗余 * 2. 重复调用每次比较都重复调用getByIndex解析字符串 * 3. 链式深度多个thenComparing调用可能导致代码行较长 * 4. 异常处理Lambda表达式中的异常处理相对复杂 * 5. 重复解析问题与方法二一样存在重复解析的性能问题 */ // 方法四集合排序先正向Comparator再Collections.reverse log.info( 集合排序先正向Comparator再Collections.reverse ); ListString sortedByReverse new ArrayList(pureList); sortedByReverse.sort(Comparator .comparing((String row) - getByIndex(row, 0)).reversed() .thenComparing(row - getByIndex(row, 02)).reversed() .thenComparing(row - getByIndex(row, 03)).reversed() .thenComparing(row - getByIndex(row, 04)) .thenComparing(row - getByIndex(row, 05)) .thenComparing(row - getByIndex(row, 8)).reversed()); Collections.reverse(sortedByReverse); sortedByReverse.forEach(row - log.info(先升序后Collections.reverse: {}, row)); /** * 方法四优缺点分析 * * 优点 * 1. 简单直观先按一种规则排序再整体反转逻辑容易理解 * 2. 适合场景当需要完全反转排序结果时非常方便 * 3. 易于调试可以先验证正向排序是否正确再考虑反转 * * 缺点 * 1. 逻辑错误这是错误的实现方式混合升序降序的排序规则不能简单反转 * 2. 结果错误会导致排序结果完全不符合预期所有列的排序方向都被反转 * 3. 性能浪费额外的Collections.reverse操作增加了时间复杂度 * 4. 不适用场景仅适用于全部升序要转为全部降序的简单场景 * 5. 维护陷阱容易被误认为是优化的排序方式实际上会产生错误结果 * * 注意此方法演示了常见的排序错误不要在实际代码中使用 */ // 方法五关键元组法构建String元组再倒序 log.info( 集合排序关键元组法构建String元组再倒序 ); ListString sortedByTuple new ArrayList(pureList); sortedByTuple.sort((row1, row2) - { // 构建正向元组未使用 String tuple1 getByIndex(row1, 0) | getByIndex(row1, 02) | getByIndex(row1, 03) | getByIndex(row1, 04) | getByIndex(row1, 05) | getByIndex(row1, 8); String tuple2 getByIndex(row2, 0) | getByIndex(row2, 02) | getByIndex(row2, 03) | getByIndex(row2, 04) | getByIndex(row2, 05) | getByIndex(row2, 8); // 构建反向元组对需要降序的列进行反转 String reverseTuple1 reverseValue(getByIndex(row1, 0)) | reverseValue(getByIndex(row1, 02)) | reverseValue(getByIndex(row1, 03)) | getByIndex(row1, 04) | getByIndex(row1, 05) | reverseValue(getByIndex(row1, 8)); String reverseTuple2 reverseValue(getByIndex(row2, 0)) | reverseValue(getByIndex(row2, 02)) | reverseValue(getByIndex(row2, 03)) | getByIndex(row2, 04) | getByIndex(row2, 05) | reverseValue(getByIndex(row2, 8)); return reverseTuple1.compareTo(reverseTuple2); }); sortedByTuple.forEach(row - log.info(关键元组法: {}, row)); /** * 方法五优缺点分析 * * 优点 * 1. 理论创新通过字符串反转巧妙实现降序思路独特 * 2. 一次比较将多个列组合成一个字符串进行一次比较减少多次函数调用 * 3. 可优化空间可以预先计算和缓存元组避免重复解析 * * 缺点 * 1. 性能开销每次比较都需要构建两个元组字符串包含多次字符串拼接和反转操作 * 2. 内存浪费每个元组字符串都需要额外内存大数据量时内存消耗严重 * 3. 字符串长度元组字符串长度取决于列值长度可能影响比较性能 * 4. 局限性强只适用于字符串比较数值类型需要额外的转换处理 * 5. 可读性差通过字符串反转实现降序的逻辑不够直观难以理解 * 6. 维护困难如果列数或排序规则变化需要修改元组构建逻辑 * 7. 错误风险reverseValue函数对null值处理、特殊字符处理等需要额外考虑 */ // 方法六自定义Comparator静态方法封装 log.info( 集合排序自定义Comparator静态方法封装 ); ListString sortedByCustomComparator new ArrayList(pureList); sortedByCustomComparator.sort(SortUtils::multiColumnComparatorMixed); sortedByCustomComparator.forEach(row - log.info(自定义Comparator封装: {}, row)); /** * 方法六优缺点分析 * * 优点 * 1. 职责分离将排序逻辑从业务代码中分离符合单一职责原则 * 2. 可测试性强独立的静态方法便于单元测试和验证 * 3. 代码复用Comparator可以在不同场景复用提高代码复用性 * 4. 易于维护排序逻辑集中在一个方法中修改和维护更方便 * 5. 性能优化可以在方法内部添加缓存机制优化重复解析问题 * 6. 可读性好方法名称明确表达排序意图代码语义清晰 * 7. 扩展性强可以通过参数配置不同的排序规则提高灵活性 * * 缺点 * 1. 方法引用限制使用方法引用时参数和返回值必须严格匹配Comparator接口 * 2. 调试复杂性需要跳转到独立方法查看排序逻辑调试时不够直观 * 3. 静态方法限制无法访问实例变量需要所有数据通过参数传递 * 4. 重复代码与lambda方式相比可能需要更多的样板代码 * 5. 方法数量如果排序规则多样化可能会产生多个Comparator方法 */ } /** * 多列混合排序Comparator * 排序规则日期降序、列2降序、列3降序、列4升序、列5升序、列8降序 * * param row1 第一行数据 * param row2 第二行数据 * return 比较结果负数表示row1小于row20表示相等正数表示row1大于row2 */ private static int multiColumnComparatorMixed(String row1, String row2) { int cmp; // 列0降序 cmp getByIndex(row2, 0).compareTo(getByIndex(row1, 0)); if (cmp ! 0) { return cmp; } // 列2降序 cmp getByIndex(row2, 02).compareTo(getByIndex(row1, 02)); if (cmp ! 0) { return cmp; } // 列3降序 cmp getByIndex(row2, 03).compareTo(getByIndex(row1, 03)); if (cmp ! 0) { return cmp; } // 列4升序 cmp getByIndex(row1, 04).compareTo(getByIndex(row2, 04)); if (cmp ! 0) { return cmp; } // 列5升序 cmp getByIndex(row1, 05).compareTo(getByIndex(row2, 05)); if (cmp ! 0) { return cmp; } // 列8降序 return getByIndex(row2, 8).compareTo(getByIndex(row1, 8)); } /** * 字符串反转方法用于关键元组法实现降序排序 * 通过反转字符串来改变比较结果实现降序效果 * * param value 需要反转的字符串 * return 反转后的字符串如果输入为null则返回null */ private static String reverseValue(String value) { if (value null) { return null; } return new StringBuilder(value).reverse().toString(); } }