前言
在工作上遇到一个比较奇怪的逻辑, 根据唯一字段排序并且序号不得重复。这样的逻辑很可能会涉及到批量更新, 锁表等, 并不值得推荐。当然有历史原因不得不这样做。好的程序不只建立在程序本身上, 更加建立在优秀的逻辑设计上。本文仅作参考。
实现
- 首先定义一个需要排序的实体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| package com.example.javademo.sort;
import lombok.Data;
@Data public class Node { private Integer id; private String name; private Integer sortIndex;
public Node(Integer id, String name, Integer sortIndex) { this.id = id; this.name = name; this.sortIndex = sortIndex; } }
|
- 然后就是我们的算法逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
| package com.example.javademo.sort;
import org.springframework.util.Assert; import java.util.Comparator; import java.util.List;
public class SortHandler {
private static final Integer STEP = 10;
public void updateSortIndex(List<Node> sortedNodes, Integer targetNodeId, Integer targetPosition) { Assert.isTrue(targetPosition > 0, "序号必须>0");
Node targetNode = sortedNodes.stream().filter(node -> node.getId().equals(targetNodeId)).findFirst().orElse(null); Assert.notNull(targetNode, String.format("id=%s的对象不存在", targetNodeId));
long distinctCount = sortedNodes.stream().map(Node::getSortIndex).distinct().count(); if (distinctCount < sortedNodes.size()) { System.out.println("存在重复序号"); resortIndex(sortedNodes); }
int currentIndex = sortedNodes.indexOf(targetNode); if (currentIndex == targetPosition - 1) { System.out.println("当前位置即是目标位置, 无需排序"); return; }
if (currentIndex < targetPosition - 1) { int leftIndex = targetPosition - 1; int rightIndex = targetPosition; if (rightIndex > sortedNodes.size() - 1) { int maxSortIndex = getMaxSortIndex(sortedNodes); targetNode.setSortIndex(maxSortIndex + STEP); System.out.println("目标右移, 尾部直接插入:"); log(sortedNodes); return; } Integer rightSortIndex = sortedNodes.get(rightIndex).getSortIndex(); Integer leftSortIndex = sortedNodes.get(leftIndex).getSortIndex(); if (rightSortIndex - leftSortIndex > 1) { int targetSortIndex = leftSortIndex + (rightSortIndex - leftSortIndex) / 2; targetNode.setSortIndex(targetSortIndex); System.out.println("目标右移, 间隙>1, 可以直接插入:"); log(sortedNodes); } else { System.out.println("目标右移, 间隙<1, 需要重排序"); sortedNodes.add(targetPosition - 1, sortedNodes.remove(sortedNodes.indexOf(targetNode))); resortIndex(sortedNodes); } return; }
if (currentIndex > targetPosition - 1) { int leftIndex = targetPosition - 2; int rightIndex = targetPosition - 1; if (leftIndex < 0) { int minSortIndex = getMinSortIndex(sortedNodes); if (minSortIndex > 1) { int targetSortIndex = 1 + (minSortIndex - 1) / 2; targetNode.setSortIndex(targetSortIndex); System.out.println("目标左移, 间隙>1, 头部直接插入:"); log(sortedNodes); } else { System.out.println("目标左移, 间隙<1, 需要重排序"); sortedNodes.add(targetPosition - 1, sortedNodes.remove(sortedNodes.indexOf(targetNode))); resortIndex(sortedNodes); } return; } Integer rightSortIndex = sortedNodes.get(rightIndex).getSortIndex(); Integer leftSortIndex = sortedNodes.get(leftIndex).getSortIndex(); if (rightSortIndex - leftSortIndex > 1) { int targetSortIndex = leftSortIndex + (rightSortIndex - leftSortIndex) / 2; targetNode.setSortIndex(targetSortIndex); System.out.println("目标左移, 间隙>1, 可以直接插入:"); log(sortedNodes); } else { System.out.println("目标左移, 间隙<1, 需要重排序"); sortedNodes.add(targetPosition - 1, sortedNodes.remove(sortedNodes.indexOf(targetNode))); resortIndex(sortedNodes); } return; }
}
private int getMaxSortIndex(List<Node> sortedNodes) { return sortedNodes.stream().mapToInt(Node::getSortIndex).max().orElse(1); }
private int getMinSortIndex(List<Node> sortedNodes) { return sortedNodes.stream().mapToInt(Node::getSortIndex).min().orElse(1); }
private void resortIndex(List<Node> sortedNodes) { for (int i = 0; i < sortedNodes.size(); i++) { Node node = sortedNodes.get(i); node.setSortIndex(STEP * i + 1); } System.out.println("重排序:"); log(sortedNodes); }
private void log(List<Node> sortedNodes) { sortedNodes.stream().sorted(Comparator.comparing(Node::getSortIndex)).forEach(System.out::println); } }
|
- 最后验证下结果是否复合预期
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| package com.example.javademo.sort;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList; import java.util.List;
@Slf4j public class SortTest {
public static void main(String[] args) { List<Node> nodes = initData(); System.out.println("原始数据:"); nodes.forEach(System.out::println); SortHandler sortHandler = new SortHandler(); Node targetNode = nodes.get(2); int targetPosition = 3; System.out.printf("排序计划:node%s -> position%s\n", targetNode.getId(), targetPosition); sortHandler.updateSortIndex(nodes, targetNode.getId(), targetPosition); }
private static List<Node> initData() { List<Node> data = new ArrayList<>(10); for (int i = 0; i < 5; i++) { Node node = new Node(i + 1, "node" + (i + 1), i * 2 + 1); data.add(node); } return data; } }
|
最后
本文到此结束,感谢阅读。如果您觉得不错,请关注公众号【当我遇上你】支持一下。