在python中存在range函数,效果如下:
即可指定range的step,而在Java中IntStream只能指定start, end,而正好项目中需要使用该特性,且需要对列表进行拆分,故实现了如下工具类。
工具代码
import com.mx.xxx.base.MsgRuntimeException;
import java.util.Collection;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
/**
* range工具类
*
* @Author luohq
* @Date 2020/08/31
*/
public final class RangeUtils {
private RangeUtils() {
}
/**
* 整数相除,对商进行向上取整
*
* @param dividend
* @param divisor
* @return
*/
private static Integer ceilDivide(Integer dividend, Integer divisor) {
int remainder = dividend % divisor;
int quotient = dividend / divisor;
int ceilQuotient = (0 == remainder ? quotient : quotient + 1);
return ceilQuotient;
}
/**
* 生成整数的rangeStream<br/>
* 注:要求start < endExlude,且step > 0 <br/>
* <p>
* 例:
* <ul>
* <li>rangeStream(0, 10, 2) ==> 0, 2, 4, 6, 8</li>
* <li>rangeStream(0, 10, 3) ==> 0, 3, 6, 9</li>
* <li>rangeStream(0, 10, 4) ==> 0, 4, 8</li>
* </ul>
*
* @param start 起始边界
* @param endExclude 结束边界(不包括)
* @param step 步长
* @return Stream<Integer>
*/
public static Stream<Integer> rangeStream(Integer start, Integer endExclude, Integer step) {
//校验参数
checkStartAndEndIndex(start, endExclude, step);
//计算批次数量
Integer stepCount = ceilDivide((endExclude - start), step);
//生成range stream
return IntStream.range(0, stepCount).mapToObj(stepIndex -> stepIndex * step);
}
/**
* 生成整数的rangeStream且返回具体索引(stepIndex, startIndex, endIndex)<br/>
* 注:要求start < endExlude,且step > 0 <br/>
* <p>
* 例:
* <ul>
* <li>rangeStream(0, 10, 2) ==> (0,0,1), (1,2,3), (2,4,5), (3,6,7), (4,8,9)</li>
* <li>rangeStream(0, 10, 3) ==> (0,0,2), (1,3,5), (2,6,8), (3,9,9)</li>
* <li>rangeStream(0, 10, 4) ==> (0,0,3), (1,4,7), (2,8,9)</li>
* </ul>
*
* @param start 起始边界
* @param endExclude 结束边界(不包括)
* @param step 步长
* @return Stream<RangeIndex(stepIndex, startIndex, endIndex)>
*/
public static Stream<RangeIndex> rangeStreamWithIndex(Integer start, Integer endExclude, Integer step) {
//校验参数
checkStartAndEndIndex(start, endExclude, step);
//计算批次数量
Integer stepCount = ceilDivide((endExclude - start), step);
//生成range stream
return IntStream.range(0, stepCount).mapToObj(stepIndex -> {
Integer stepStartIndex = stepIndex * step;
Integer stepEndIndex = Math.min(stepStartIndex + step, endExclude);
return new RangeIndex(stepIndex, stepStartIndex, stepEndIndex);
});
}
/**
* 执行list的rangeStream,并同过consumer处理集合拆分后的子range集合<br/>
*
* @param list 列表集合
* @param start 起始边界
* @param endExclude 结束边界(不包括)
* @param step 步长
* @param handlerRangeStream 处理每个range的consumer
* @param <T>
*/
public static <T extends List> void rangeStream(T list, Integer start, Integer endExclude, Integer step, Consumer<T> handlerRangeStream) {
//校验集合
checkCollection(list);
//校验非空
checkEmpty(handlerRangeStream, "consumer");
//执行rangeStream
rangeStreamWithIndex(start, endExclude, step).forEach(rangeIndex -> {
T subList = (T) list.subList(rangeIndex.getStartIndex(), rangeIndex.getStopIndexExclude());
handlerRangeStream.accept(subList);
});
}
/**
* 执行list的rangeStream,并同过biConsumer处理集合拆分后的子range集合,同时返回具体索引(stepIndex, startIndex, endIndex)<br/>
*
* @param list 列表集合
* @param start 起始边界
* @param endExclude 结束边界(不包括)
* @param step 步长
* @param handlerRangeStream 处理每个range的biConsumer
* @param <T>
*/
public static <T extends List> void rangeStream(T list, Integer start, Integer endExclude, Integer step, BiConsumer<RangeIndex, T> handlerRangeStream) {
//校验集合
checkCollection(list);
//校验非空
checkEmpty(handlerRangeStream, "consumer");
//执行rangeStream
rangeStreamWithIndex(start, endExclude, step).forEach(rangeIndex -> {
T subList = (T) list.subList(rangeIndex.getStartIndex(), rangeIndex.getStopIndexExclude());
handlerRangeStream.accept(rangeIndex, subList);
});
}
/**
* 执行list的rangeStream,并同过consumer处理子range集合<br/>
* 注:start=0, endExclue=list.size()
*
* @param list 列表集合
* @param step 步长
* @param handlerRangeStream 处理每个range的consumer
* @param <T>
*/
public static <T extends List> void rangeStream(T list, Integer step, Consumer<T> handlerRangeStream) {
//校验集合
checkCollection(list);
//执行rangeStream(遍历整个list)
rangeStream(list, 0, list.size(), step, handlerRangeStream);
}
/**
* 执行list的rangeStream,并同过biConsumer处理子range集合,且返回具体索引(stepIndex, startIndex, endIndex)<br/>
* 注:start=0, endExclue=list.size()
*
* @param list 列表集合
* @param step 步长
* @param handlerRangeStream 处理每个range的biConsumer
* @param <T>
*/
public static <T extends List> void rangeStream(T list, Integer step, BiConsumer<RangeIndex, T> handlerRangeStream) {
//校验集合
checkCollection(list);
//执行rangeStream(遍历整个list)
rangeStream(list, 0, list.size(), step, handlerRangeStream);
}
/**
* 验证起止索引、step
*
* @param start
* @param stopExclude
* @param step
* @param <T>
*/
private static <T extends Collection> void checkStartAndEndIndex(Integer start, Integer stopExclude, Integer step) {
if (null == start || null == stopExclude || null == step) {
throw new MsgRuntimeException("自定义rangeStream起止索引、步长不能为空!");
}
if (0 > start || 0 > stopExclude || 0 >= step) {
throw new MsgRuntimeException("自定义rangeStream起止索引、步长(不可等于0)均需要>=0!");
}
if (start >= stopExclude || step > (stopExclude - start)) {
throw new MsgRuntimeException("自定义rangeStream起止索引、步长不符合规范(start < stopExclude 且 step <= (stopExclude - start))");
}
}
/**
* 验证集合非空
*
* @param collection
* @param <T>
*/
private static <T extends Collection> void checkCollection(T collection) {
if (null == collection || 0 >= collection.size()) {
throw new MsgRuntimeException("自定义rangeStream 待处理集合不能为空!");
}
}
/**
* 验证对象非空
*
* @param obj
* @param objDesc
* @param <T>
*/
private static <T> void checkEmpty(T obj, String objDesc) {
if (null == obj) {
throw new MsgRuntimeException("自定义rangeStream 待处理".concat(objDesc).concat("不能为空!"));
}
}
/**
* range索引
*/
public static class RangeIndex {
/**
* 当前range的批次索引(第几个range,从0开始)
*/
private Integer stepIndex;
/**
* 当前range的起始索引
*/
private Integer startIndex;
/**
* 当前range的结束索引(不包括当前索引)
*/
private Integer stopIndexExclude;
public RangeIndex(Integer stepIndex, Integer startIndex, Integer stopIndexExclude) {
this.stepIndex = stepIndex;
this.startIndex = startIndex;
this.stopIndexExclude = stopIndexExclude;
}
public Integer getStepIndex() {
return stepIndex;
}
public Integer getStartIndex() {
return startIndex;
}
public Integer getStopIndexExclude() {
return stopIndexExclude;
}
@Override
public String toString() {
return stepIndex + "[" + startIndex + ", " + stopIndexExclude + ")";
}
}
}
测试用例
public static void main(String[] args) {
int start = 0, endExclude = 10;
//设置step=2, 3, 4
IntStream.range(2, 5).forEach(step -> {
System.out.println(String.format("<==== 测试rangeStream start=%s, endExclude=%s, step=%s ====>", start, endExclude, step));
/** 测试rangeStream */
String indexStr = RangeUtils.rangeStream(start, endExclude, step)
.map(String::valueOf)
.collect(Collectors.joining(","));
System.out.println(String.format("start=%s, endExclude=%s, step=%s, indexArray=%s\n", start, endExclude, step, indexStr));
});
//设置step=2, 3, 4
IntStream.range(2, 5).forEach(step -> {
System.out.println(String.format("<==== 测试rangeStreamWithIndex start=%s, endExclude=%s, step=%s ====>", start, endExclude, step));
/** 测试rangeStreamWithIndex */
String indexStr = RangeUtils.rangeStreamWithIndex(start, endExclude, step)
.map(RangeIndex::toString)
.collect(Collectors.joining(","));
System.out.println(String.format("start=%s, endExclude=%s, step=%s, indexArray=%s\n", start, endExclude, step, indexStr));
});
//定义待拆分集合
List<Integer> numList = IntStream.range(0, 10).mapToObj(Integer::valueOf).collect(Collectors.toList());
//设置step=2, 3, 4
IntStream.range(2, 5).forEach(step -> {
System.out.println(String.format("<==== 测试rangeStream(仅获取拆分后列表) start=%s, endExclude=%s, step=%s ====>", start, endExclude, step));
/** 测试rangeStream(仅获取拆分后列表) */
RangeUtils.rangeStream(numList, start, endExclude, step, (subNumList) -> {
System.out.println(String.format("%s", subNumList));
});
System.out.println();
});
//设置step=2, 3, 4
IntStream.range(2, 5).forEach(step -> {
System.out.println(String.format("<==== 测试rangeStream(同时获取拆分索引和获取拆分后列表) start=%s, endExclude=%s, step=%s ====>", start, endExclude, step));
/** 测试rangeStream(同时获取拆分索引和获取拆分后列表) */
RangeUtils.rangeStream(numList, start, endExclude, step, (rangeIndex, subNumList) -> {
System.out.println(String.format("%s=%s", rangeIndex, subNumList));
});
System.out.println();
});
}
测试结果
<==== 测试rangeStream start=0, endExclude=10, step=2 ====>
start=0, endExclude=10, step=2, indexArray=0,2,4,6,8
<==== 测试rangeStream start=0, endExclude=10, step=3 ====>
start=0, endExclude=10, step=3, indexArray=0,3,6,9
<==== 测试rangeStream start=0, endExclude=10, step=4 ====>
start=0, endExclude=10, step=4, indexArray=0,4,8
<==== 测试rangeStreamWithIndex start=0, endExclude=10, step=2 ====>
start=0, endExclude=10, step=2, indexArray=0[0, 2),1[2, 4),2[4, 6),3[6, 8),4[8, 10)
<==== 测试rangeStreamWithIndex start=0, endExclude=10, step=3 ====>
start=0, endExclude=10, step=3, indexArray=0[0, 3),1[3, 6),2[6, 9),3[9, 10)
<==== 测试rangeStreamWithIndex start=0, endExclude=10, step=4 ====>
start=0, endExclude=10, step=4, indexArray=0[0, 4),1[4, 8),2[8, 10)
<==== 测试rangeStream(仅获取拆分后列表) start=0, endExclude=10, step=2 ====>
[0, 1]
[2, 3]
[4, 5]
[6, 7]
[8, 9]
<==== 测试rangeStream(仅获取拆分后列表) start=0, endExclude=10, step=3 ====>
[0, 1, 2]
[3, 4, 5]
[6, 7, 8]
[9]
<==== 测试rangeStream(仅获取拆分后列表) start=0, endExclude=10, step=4 ====>
[0, 1, 2, 3]
[4, 5, 6, 7]
[8, 9]
<==== 测试rangeStream(同时获取拆分索引和获取拆分后列表) start=0, endExclude=10, step=2 ====>
0[0, 2)=[0, 1]
1[2, 4)=[2, 3]
2[4, 6)=[4, 5]
3[6, 8)=[6, 7]
4[8, 10)=[8, 9]
<==== 测试rangeStream(同时获取拆分索引和获取拆分后列表) start=0, endExclude=10, step=3 ====>
0[0, 3)=[0, 1, 2]
1[3, 6)=[3, 4, 5]
2[6, 9)=[6, 7, 8]
3[9, 10)=[9]
<==== 测试rangeStream(同时获取拆分索引和获取拆分后列表) start=0, endExclude=10, step=4 ====>
0[0, 4)=[0, 1, 2, 3]
1[4, 8)=[4, 5, 6, 7]
2[8, 10)=[8, 9]