Java百分比计算处理工具

1.说明

此工具可以帮助处理一个表格中各个百分比累计不等于100的问题

设一个m*n的表格(m行n列),具体可以处理以下3种类型:

2.横向处理(名称含有"XAxis"的方法)
  • 保证1到n-1列累加值=第n列的值

∑ j = 1 n − 1 ( x i , y j ) = x i y n \sum_{j=1}^{n-1}(x_i, y_j) = x_iy_n j=1n1(xi,yj)=xiyn

  • 或者保证1到n列累加值=100

∑ j = 1 n ( x i , y j ) = 100 \sum_{j=1}^n(x_i, y_j)=100 j=1n(xi,yj)=100

3.纵向处理(名称含有"YAxis"的方法)
  • 保证1到m-1行累加值=第m行的值

∑ i = 1 m − 1 ( x i , y j ) = x m y j \sum_{i=1}^{m-1}(x_i, y_j) = x_my_j i=1m1(xi,yj)=xmyj

  • 或者保证1到m行累加值=100

∑ i = 1 m ( x i , y j ) = 100 \sum_{i=1}^m(x_i, y_j) = 100 i=1m(xi,yj)=100

3.交叉处理(名称含有"cross"的方法)
  • 横向处理最后一行(第m行),纵向处理最后一列(第n列),保证最后一行1到n-1列累加值=(m,n)的值,最后一列1到m-1行累加值=(m,n)的值。

∑ j = 1 n − 1 ( x m , y j ) = x m y n \sum_{j=1}^{n-1}(x_m, y_j) = x_my_n j=1n1(xm,yj)=xmyn

∑ i = 1 m − 1 ( x i , y n ) = x m y n \sum_{i=1}^{m-1}(x_i, y_n) = x_my_n i=1m1(xi,yn)=xmyn

4.源码
package com.visy.utils;

import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Function;

/**
 * 百分比工具
 * @author visy.wang
 * @date 2024/8/13 13:03
 */
public class PercentageUtil {
    /**
     * 获取百分比(含四舍五入后两位小数部分)
     * @param a 分子
     * @param b 分母
     * @return 百分比
     */
    public static double getPercent(int a, int b){
        if(b == 0){
            return a==0 ? 0 : 100; // a/0 = 100%; 0/0 = 0%
        }else if(a == b){
            return 100; // a/a = 100%
        }else if(a == 0){
            return 0; // 0/b = 0%
        }
        //计算百分比,保留2位小数
        return BigDecimal.valueOf(a * 100L).divide(BigDecimal.valueOf(b), 2, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 百分比修正
     * @param matrix 原始矩阵
     * @param isReversed 是否反转矩阵(即将x和y轴互换),true-横向,false-纵向
     * @param includesLastLine 是否包含最后一行或列(反转后),不包含时以最后一行或列的值作为总计百分比,不包含则总计百分比按100处理
     * @param getter 获取单元格的百分比(内容为数字,不能含有非数字的字符)
     * @param setter 设置单元格的百分比,与converter二选一,优先级高于converter
     * @param converter 将百分比数字转换成需要的类型,与setter二选一
     * @param <T> 矩阵元素类型
     */
    private static <T> void correcting(
        T[][] matrix,
        boolean isReversed,
        boolean includesLastLine,
        Function<T,String> getter,
        BiConsumer<T,String> setter,
        Function<String,T> converter
    ){
        //原始矩阵的长和宽
        int xLength = matrix.length, yLength = matrix[0].length;
        //处理矩阵x轴和y轴的反转
        int iLength = isReversed ? yLength : xLength;
        int jLength = isReversed ? xLength : yLength;
        //保留或移除最后一行和列
        jLength = includesLastLine ? jLength : jLength-1;
        //遍历矩阵
        for(int i=0; i<iLength; i++){
            int lastOfJ = 0; //记录最后一个有占比单元格的行或列下标
            BigDecimal percentSum = BigDecimal.valueOf(0);//累计百分比

            for(int j=0; j<jLength; j++){
                T cell = isReversed ? matrix[j][i] : matrix[i][j];
                //获取当前项的百分比
                BigDecimal percent = new BigDecimal(getter.apply(cell));
                if(percent.doubleValue() > 0){
                    lastOfJ = j; //记录下标
                    //累加百分比
                    percentSum = percentSum.add(percent);
                }
            }

            //获取当前行或列的累计百分比
            if(percentSum.doubleValue() == 0){
                //累计百分比=0,无需特殊处理
                continue;
            }

            BigDecimal total; //总计百分比
            if(includesLastLine){
                //含最有一行或列的情况下,总计百分比取100
                total = BigDecimal.valueOf(100);
            }else{
                //不含最有一行或列的情况下,以最后一行或列的值作为总计百分比
                T sumCell = isReversed ? matrix[jLength][i] : matrix[i][jLength];
                total = new BigDecimal(getter.apply(sumCell)) ;
            }

            //计算累计百分比是否超过或小于总计百分比
            BigDecimal difference = total.subtract(percentSum);
            if(difference.doubleValue() == 0){
                //累计百分比刚好=总计百分比,无需特殊处理
                continue;
            }

            //累计百分比!=100,调整最后一个有占比的单元格,使得总百分比=100
            T cell = isReversed ? matrix[lastOfJ][i] : matrix[i][lastOfJ];
            String newValue = new BigDecimal(getter.apply(cell)).add(difference).toString();
            if(setter != null){
                //更新单元格属性
                setter.accept(cell, newValue);
            }else if(converter != null){
                //更新整个单元格
                if(isReversed){
                    matrix[lastOfJ][i] = converter.apply(newValue);
                }else{
                    matrix[i][lastOfJ] = converter.apply(newValue);
                }
            }
        }
    }

    /**
     * 百分比修正
     * @param matrix 原始矩阵
     * @param isReversed 是否反转矩阵(即将x和y轴互换),true-横向,false-纵向
     * @param includesLastLine 是否包含最后一行或列(反转后)
     * @param getter 获取单元格的百分比(内容为数字,不能含有非数字的字符)
     * @param setter 设置单元格的百分比
     * @param <T> 矩阵元素类型
     */
    private static <T> void correcting(
        T[][] matrix,
        boolean isReversed,
        boolean includesLastLine,
        Function<T,String> getter,
        BiConsumer<T,String> setter
    ){
        correcting(matrix, isReversed, includesLastLine, getter, setter, null);
    }

    /**
     * 百分比修正
     * @param matrix 原始矩阵
     * @param isReversed 是否反转矩阵(即将x和y轴互换),true-横向,false-纵向
     * @param includesLastLine 是否包含最后一行或列(反转后)
     * @param converter 将百分比数字转换成需要的类型
     * @param <T> 矩阵元素类型
     */
    private static <T> void correcting(
        T[][] matrix,
        boolean isReversed,
        boolean includesLastLine,
        Function<String,T> converter
    ){
        correcting(matrix, isReversed, includesLastLine, String::valueOf, null, converter);
    }

    /**
     * 百分比修正(横向,不包含最有一列)
     * Before:
     * -------------------------------
     * 1	2	3	6
     * 1	4	3	6
     * 1	1	3	6
     * 1	2	7	6
     * 4	11	10	24
     * -------------------------------
     * After:
     * -------------------------------
     * 1	2	3	6
     * 1	4	1*	6
     * 1	1	4*	6
     * 1	2	3*	6
     * 4	11	9*	24
     * -------------------------------
     * @param matrix 原始矩阵
     * @param getter 获取单元格的百分比(内容为数字,不能含有非数字的字符)
     * @param setter 设置单元格的百分比
     * @param <T> 矩阵元素类型
     */
    public static <T> void correctingXAxis(
        T[][] matrix,
        Function<T,String> getter,
        BiConsumer<T,String> setter
    ){
        correcting(matrix, false, false, getter, setter);
    }

    /**
     * 百分比修正(横向,包含最后一列)
     * Before:
     * -------------------------------
     * 1	2	3	6
     * 1	4	3	6
     * 1	1	3	6
     * 1	2	7	6
     * 4	11	10	24
     * -------------------------------
     * After:
     * -------------------------------
     * 1	2	3	94*
     * 1	4	3	92*
     * 1	1	3	95*
     * 1	2	7	90*
     * 4	11	10	75*
     * -------------------------------
     * @param matrix 原始矩阵
     * @param getter 获取单元格的百分比(内容为数字,不能含有非数字的字符)
     * @param setter 设置单元格的百分比
     * @param <T> 矩阵元素类型
     */
    public static <T> void correctingXAxisWithLastLine(
        T[][] matrix,
        Function<T,String> getter,
        BiConsumer<T,String> setter
    ){
        correcting(matrix, false, true, getter, setter);
    }

    /**
     * 百分比修正(纵向,不包含最后一行)
     * Before:
     * -------------------------------
     * 1	2	3	6
     * 1	4	3	6
     * 1	1	3	6
     * 1	2	7	6
     * 4	11	10	24
     * -------------------------------
     * After:
     * -------------------------------
     * 1	2	3	6
     * 1	4	3	6
     * 1	1	3	6
     * 1	4*	1*	6
     * 4	11	10	24
     * -------------------------------
     * @param matrix 原始矩阵
     * @param getter 获取单元格的百分比(内容为数字,不能含有非数字的字符)
     * @param setter 设置单元格的百分比
     * @param <T> 矩阵元素类型
     */
    public static <T> void correctingYAxis(
        T[][] matrix,
        Function<T,String> getter,
        BiConsumer<T,String> setter
    ){
        correcting(matrix, true, false, getter, setter);
    }

    /**
     * 百分比修正(纵向,包含最后一行)
     * Before:
     * -------------------------------
     * 1	2	3	6
     * 1	4	3	6
     * 1	1	3	6
     * 1	2	7	6
     * 4	11	10	24
     * -------------------------------
     * After:
     * -------------------------------
     * 1	2	3	6
     * 1	4	3	6
     * 1	1	3	6
     * 1	2	7	6
     * 96*	91*	84*	76*
     * -------------------------------
     * @param matrix 原始矩阵
     * @param getter 获取单元格的百分比(内容为数字,不能含有非数字的字符)
     * @param setter 设置单元格的百分比
     * @param <T> 矩阵元素类型
     */
    public static <T> void correctingYAxisWithLastLine(
        T[][] matrix,
        Function<T,String> getter,
        BiConsumer<T,String> setter
    ){
        correcting(matrix, true, true, getter, setter);
    }

    /**
     * 百分比修正(横向,不包含最后一列)
     * Before:
     * -------------------------------
     * 1	2	3	6
     * 1	4	3	6
     * 1	1	3	6
     * 1	2	7	6
     * 4	11	10	24
     * -------------------------------
     * After:
     * -------------------------------
     * 1	2	3	6
     * 1	4	1*	6
     * 1	1	4*	6
     * 1	2	3*	6
     * 4	11	9*	24
     * -------------------------------
     * @param matrix 原始矩阵
     * @param converter 将百分比数字转换成需要的类型
     * @param <T> 矩阵元素类型
     */
    public static <T> void correctingXAxis(
        T[][] matrix,
        Function<String,T> converter
    ){
        correcting(matrix, false, false, converter);
    }

    /**
     * 百分比修正(横向,包含最后一列)
     * Before:
     * -------------------------------
     * 1	2	3	6
     * 1	4	3	6
     * 1	1	3	6
     * 1	2	7	6
     * 4	11	10	24
     * -------------------------------
     * After:
     * -------------------------------
     * 1	2	3	94*
     * 1	4	3	92*
     * 1	1	3	95*
     * 1	2	7	90*
     * 4	11	10	75*
     * -------------------------------
     * @param matrix 原始矩阵
     * @param converter 将百分比数字转换成需要的类型
     * @param <T> 矩阵元素类型
     */
    public static <T> void correctingXAxisWithLastLine(
        T[][] matrix,
        Function<String,T> converter
    ){
        correcting(matrix, false, true, converter);
    }

    /**
     * 百分比修正(纵向,不包含最后一行)
     * Before:
     * -------------------------------
     * 1	2	3	6
     * 1	4	3	6
     * 1	1	3	6
     * 1	2	7	6
     * 4	11	10	24
     * -------------------------------
     * After:
     * -------------------------------
     * 1	2	3	6
     * 1	4	3	6
     * 1	1	3	6
     * 1	4*	1*	6
     * 4	11	10	24
     * -------------------------------
     * @param matrix 原始矩阵
     * @param converter 将百分比数字转换成需要的类型
     * @param <T> 矩阵元素类型
     */
    public static <T> void correctingYAxis(
        T[][] matrix,
        Function<String,T> converter
    ){
        correcting(matrix, true, false, converter);
    }

    /**
     * 百分比修正(纵向,包含最后一行)
     * Before:
     * -------------------------------
     * 1	2	3	6
     * 1	4	3	6
     * 1	1	3	6
     * 1	2	7	6
     * 4	11	10	24
     * -------------------------------
     * After:
     * -------------------------------
     * 1	2	3	6
     * 1	4	3	6
     * 1	1	3	6
     * 1	2	7	6
     * 96*	91*	84*	76*
     * -------------------------------
     * @param matrix 原始矩阵
     * @param converter 将百分比数字转换成需要的类型
     * @param <T> 矩阵元素类型
     */
    public static <T> void correctingYAxisWithLastLine(
        T[][] matrix,
        Function<String,T> converter
    ){
        correcting(matrix, true, true, converter);
    }

    /**
     * 百分比修正(交叉类型)
     * 只修正最后一行和最后一列
     * @param matrix 原始矩阵
     * @param getter 获取单元格的百分比(内容为数字,不能含有非数字的字符)
     * @param setter 设置单元个的百分比,与converter二选一,优先级高于converter
     * @param converter 将百分比数字转换成需要的类型,与setter二选一
     * @param <T> 矩阵元素类型
     */
    private static <T> void correctingCross(
        T[][] matrix,
        Function<T,String> getter,
        BiConsumer<T,String> setter,
        Function<String,T> converter
    ){
        //原始矩阵的长和宽
        int xLength = matrix.length, yLength = matrix[0].length;

        int lastIndex = 0; //累计百分比时最后一个有占比的单元格下标位置
        BigDecimal percentSum = BigDecimal.valueOf(0); //累计百分比

        //遍历最后一行
        for(int y=0; y<yLength-1; y++){
            BigDecimal value = new BigDecimal(getter.apply(matrix[xLength-1][y]));
            if(value.doubleValue() > 0){
                lastIndex = y;
                percentSum = percentSum.add(value);
            }
        }

        //获取总计百分比
        T sumCell = matrix[xLength-1][yLength-1];
        BigDecimal total = new BigDecimal(getter.apply(sumCell));

        //计算最后一行累计百分比和总计百分比的差值
        BigDecimal difference = total.subtract(percentSum);
        if(difference.doubleValue() != 0){
            //有差值,则调整最后一个有占比的单元格
            T cell = matrix[xLength-1][lastIndex];
            String newValue = new BigDecimal(getter.apply(cell)).add(difference).toString();
            if(setter != null){
                setter.accept(cell, newValue);
            }else if(converter != null){
                matrix[xLength-1][lastIndex] = converter.apply(newValue);
            }
        }

        //遍历最后一列
        lastIndex = 0;
        percentSum = BigDecimal.valueOf(0);
        for(int x=0; x<xLength-1; x++){
            BigDecimal value = new BigDecimal(getter.apply(matrix[x][yLength-1]));
            if(value.doubleValue() > 0){
                lastIndex = x;
                percentSum = percentSum.add(value);
            }
        }

        //计算最后一列累计百分比和总计百分比的差值
        difference = total.subtract(percentSum);
        if(difference.doubleValue() != 0){
            //有差值,则调整最后一个有占比的单元格
            T cell = matrix[lastIndex][yLength-1];
            String newValue = new BigDecimal(getter.apply(cell)).add(difference).toString();
            if(setter != null){
                setter.accept(cell, newValue);
            }else if(converter != null){
                matrix[lastIndex][yLength-1] = converter.apply(newValue);
            }
        }
    }

    /**
     * 百分比修正(交叉类型)
     * Before:
     * -------------------------------
     * 1	2	3	6
     * 1	2	3	6
     * 2	2	3	7
     * 1	2	3	6
     * 5	8	12	24
     * -------------------------------
     * After:
     * -------------------------------
     * 1	2	3	6
     * 1	2	3	6
     * 2	2	3	7
     * 1	2	3	5*
     * 5	8	11*	24
     * -------------------------------
     * @param matrix 原始矩阵
     * @param getter 获取单元格的百分比(内容为数字,不能含有非数字的字符)
     * @param setter 设置单元个的百分比
     * @param <T> 矩阵元素类型
     */
    public static <T> void correctingCross(
        T[][] matrix,
        Function<T,String> getter,
        BiConsumer<T,String> setter
    ){
        correctingCross(matrix, getter, setter, null);
    }

    /**
     * 百分比修正(交叉类型)
     * Before:
     * -------------------------------
     * 1	2	3	6
     * 1	2	3	6
     * 2	2	3	7
     * 1	2	3	6
     * 5	8	12	24
     * -------------------------------
     * After:
     * -------------------------------
     * 1	2	3	6
     * 1	2	3	6
     * 2	2	3	7
     * 1	2	3	5*
     * 5	8	11*	24
     * -------------------------------
     * @param matrix 原始矩阵
     * @param converter 将百分比数字转换成需要的类型
     * @param <T> 矩阵元素类型
     */
    public static <T> void correctingCross(
        T[][] matrix,
        Function<String,T> converter
    ){
        correctingCross(matrix, String::valueOf, null, converter);
    }

    /**
     * 双层List转矩阵
     * @param list 双层list
     * @param tClass 数组元素类型
     * @return 矩阵
     * @param <T> 元素类型
     */
    @SuppressWarnings("unchecked")
    public static <T> T[][] toMatrix(List<List<T>> list, Class<? super T> tClass){
        int xLength = list.size();
        int yLength = list.stream().mapToInt(List::size).max().orElse(0);

        T[][] matrix = (T[][]) Array.newInstance(tClass, xLength, yLength);
        for(int x=0; x<xLength; x++){
            List<T> lst = list.get(x);
            for(int y=0; y<lst.size(); y++){
                matrix[x][y] = lst.get(y);
            }
        }
        return matrix;
    }

    /**
     * 将列表转换为一行
     * @param list 列表
     * @param tClass 数组元素类型
     * @return 矩阵(只有一行)
     * @param <T> 元素类型
     */
    @SuppressWarnings("unchecked")
    public static <T> T[][] toOneRow(List<T> list, Class<? super T> tClass){
        int yLength = list.size();
        T[][] matrix = (T[][]) Array.newInstance(tClass, 1, yLength);
        for(int y=0; y<yLength; y++){
            matrix[0][y] = list.get(y);
        }
        return matrix;
    }
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值