java-poi实现excel自定义注解生成数据并导出

news2024/11/15 10:16:30

        因为项目很多地方需要使用导出数据excel的功能,所以开发了一个简易的统一生成导出方法。

依赖

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>4.0.1</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>4.0.1</version>
</dependency>

定义注解

标题栏注解 

import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.IndexedColors;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelTitle {

    /**
     * 标题名称
     * @return 默认空
     */
    String titleName() ;

    /**
     * 标题背景
     * @return 默认空
     */
    IndexedColors titleBack() default IndexedColors.WHITE;

    /**
     * 标题文字大小
     * @return 默认空
     */
    short titleSize() default 14;

    /**
     * 标题文字颜色
     * @return 黑色
     */
    HSSFColor.HSSFColorPredefined titleColor() default HSSFColor.HSSFColorPredefined.BLACK;

    /**
     * 边框格式
     * @return 细
     */
    BorderStyle borderStyle() default BorderStyle.THIN;

    /**
     * 边框颜色
     * @return 默认
     */
    IndexedColors borderColor() default IndexedColors.AUTOMATIC;

    /**
     * 标题文字加粗
     * @return 黑色
     */
    boolean boldFont() default true;

    /**
     * 是否忽略
     * @return 黑色
     */
    boolean ignore() default false;

    /**
     * 排序
     * @return 0
     */
    int order() default 0;
}

数据栏注解

import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.VerticalAlignment;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @description: excel导出结果配置
 */
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExcelProperty {

    /**
     * 背景
     * @return 默认空
     */
    IndexedColors textBack() default IndexedColors.WHITE;

    /**
     * 内容类型,查看
     * @link org.apache.poi.ss.usermodel.BuiltinFormats
     * @return 默认TEXT
     */
    String textType() default "TEXT";

    /**
     * 文字大小
     * @return 默认18
     */
    short textSize() default 12;

    /**
     * 数据属于key:value时,可以自定义转换,配置格式为:key1=value;key2=value2;.....
     * @return 默认空
     */
    String textKv() default "";

    /**
     * 文字颜色
     * @return 黑色
     */
    HSSFColor.HSSFColorPredefined textColor() default HSSFColor.HSSFColorPredefined.BLACK;

    /**
     * 水平位置
     * @return 水平居中
     */
    HorizontalAlignment horizontal() default HorizontalAlignment.CENTER;

    /**
     * 垂直位置
     * @return 垂直居中
     */
    VerticalAlignment vertical() default VerticalAlignment.CENTER;

    /**
     * 文字加粗
     * @return 不加粗
     */
    boolean boldFont() default false;
}

代码

 标题栏格式生成

        解析对象属性的@ExcelTitle注解的信息,并设置相关选项。

private CellStyle initTitleCellStyle(SXSSFWorkbook wb, Field titleField) {
        // 单元格样式
        XSSFCellStyle cellStyle = (XSSFCellStyle) wb.createCellStyle();
        //水平居中
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        //垂直居中
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);

        ExcelTitle excelTitle = titleField.getAnnotation(ExcelTitle.class);

        //背景颜色
        if(excelTitle != null && excelTitle.titleBack() != null){
            cellStyle.setFillForegroundColor(excelTitle.titleBack().getIndex());
        }  else {
            cellStyle.setFillForegroundColor(IndexedColors.WHITE1.getIndex());
        }

        //文字的设置字体颜色
        Font font = wb.createFont();
        if(excelTitle != null && excelTitle.titleColor() != null){
            font.setColor(excelTitle.titleColor().getIndex());
        }  else {
            font.setColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());
        }
        font.setBold(excelTitle != null && excelTitle.boldFont());
        font.setFontHeightInPoints(excelTitle != null && excelTitle.titleSize() != 0 ? excelTitle.titleSize(): 12);
        cellStyle.setFont(font);

        //设置为文本格式
        cellStyle.setDataFormat(BuiltinFormats.getBuiltinFormat("TEXT"));

        cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);

        //边框
        if(excelTitle != null && excelTitle.borderStyle() != null){
            cellStyle.setBorderTop(excelTitle.borderStyle());
            cellStyle.setBorderBottom(excelTitle.borderStyle());
            cellStyle.setBorderLeft(excelTitle.borderStyle());
            cellStyle.setBorderRight(excelTitle.borderStyle());
        } else {
            cellStyle.setBorderTop(BorderStyle.THIN);
            cellStyle.setBorderBottom(BorderStyle.THIN);
            cellStyle.setBorderLeft(BorderStyle.THIN);
            cellStyle.setBorderRight(BorderStyle.THIN);
        }

        //边框
        if(excelTitle != null && excelTitle.borderColor() != null){
            cellStyle.setTopBorderColor(excelTitle.borderColor().getIndex());
            cellStyle.setBottomBorderColor(excelTitle.borderColor().getIndex());
            cellStyle.setLeftBorderColor(excelTitle.borderColor().getIndex());
            cellStyle.setRightBorderColor(excelTitle.borderColor().getIndex());
        } else {
            cellStyle.setTopBorderColor(IndexedColors.RED.getIndex());
            cellStyle.setBottomBorderColor(IndexedColors.RED.getIndex());
            cellStyle.setLeftBorderColor(IndexedColors.RED.getIndex());
            cellStyle.setRightBorderColor(IndexedColors.RED.getIndex());
        }

        return cellStyle;
    }

数据栏格式生成

  解析对象属性的@ExcelProperty注解的信息,并设置相关选项。

private CellStyle initCellStyle(SXSSFWorkbook wb, Field titleField) {
        // 单元格样式(垂直居中)
        XSSFCellStyle cellStyle = (XSSFCellStyle) wb.createCellStyle();

        ExcelProperty excelProperty = titleField.getAnnotation(ExcelProperty.class);

        //水平居中
        if(excelProperty != null && excelProperty.horizontal() != null) {
            cellStyle.setAlignment(excelProperty.horizontal());
        }
        //垂直居中
        if(excelProperty != null && excelProperty.vertical() != null) {
            cellStyle.setVerticalAlignment(excelProperty.vertical());
        }
        //背景颜色
        if(excelProperty != null && excelProperty.textBack() != null){
            cellStyle.setFillForegroundColor(excelProperty.textBack().getIndex());
        }

        //文字的设置字体颜色
        Font font = wb.createFont();
        if(excelProperty != null && excelProperty.textColor() != null){
            font.setColor(excelProperty.textColor().getIndex());
        }

        font.setBold(excelProperty != null && excelProperty.boldFont());
        font.setFontHeightInPoints(excelProperty != null && excelProperty.textSize() != 0 ? excelProperty.textSize(): 12);
        cellStyle.setFont(font);

        cellStyle.setBorderTop(BorderStyle.THIN);
        cellStyle.setBorderBottom(BorderStyle.THIN);
        cellStyle.setBorderLeft(BorderStyle.THIN);
        cellStyle.setBorderRight(BorderStyle.THIN);

        //设置为文本格式
        cellStyle.setDataFormat(BuiltinFormats.getBuiltinFormat(excelProperty != null && excelProperty.textType() != null ? excelProperty.textType() : "TEXT"));

        cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
        return cellStyle;
    }

同时提供一个默认的配置:

private CellStyle initDefaultCellStyle(SXSSFWorkbook wb) {
        // 单元格样式(垂直居中)
        XSSFCellStyle cellStyle = (XSSFCellStyle) wb.createCellStyle();
        //水平居中
        cellStyle.setAlignment(HorizontalAlignment.CENTER);
        //垂直居中
        cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
        //设置为文本格式
        cellStyle.setDataFormat(BuiltinFormats.getBuiltinFormat("TEXT"));
        //文字的设置
        Font font = wb.createFont();
        font.setColor(HSSFColor.HSSFColorPredefined.BLACK.getIndex());   //设置字体颜色
        return cellStyle;
    }

解析数据,生成sheet

private SXSSFWorkbook creatBook(List<ExcelSheetVo> list) {
        //创建工作簿
        SXSSFWorkbook wb = new SXSSFWorkbook();
        for (ExcelSheetVo excelSheetVo : list) {
            createSXSSFSheet(excelSheetVo, wb);
        }
        return wb;
    }

    private SXSSFSheet createSXSSFSheet(ExcelSheetVo excelSheetVo, SXSSFWorkbook wb) {
        //创建工作簿
        SXSSFSheet sxssfSheet = wb.createSheet(excelSheetVo.getSheetName());
        //设置默认的行宽
        sxssfSheet.setDefaultColumnWidth(20);
        //设置morning的行高(不能设置太小,可以不设置)
        sxssfSheet.setDefaultRowHeight((short) 300);
        //设置morning的单元格格式
        //CellStyle style = initCellStyle(wb);
        //标题单元格格式
        //CellStyle titleStyle = initTitleCellStyle(wb, excelSheetVo.getDataClass());

        //初始化标题栏
        initTitle(wb, sxssfSheet, excelSheetVo.getDataClass());
        initSheetData(wb, sxssfSheet, excelSheetVo, 1);

        return sxssfSheet;
    }

    private void initSheetData(SXSSFWorkbook wb, SXSSFSheet sxssfSheet, ExcelSheetVo excelSheetVo, int dataStartLine) {
        if (CollectionUtils.isEmpty(excelSheetVo.getSheetData())) {
            return;
        }
        try {
            for (Object dataObject : excelSheetVo.getSheetData()) {
                SXSSFRow row = sxssfSheet.createRow(dataStartLine);

                Field[] fields = dataObject.getClass().getDeclaredFields();
                List<Field> fieldList = Arrays.stream(fields)
                        .filter(item -> item.getAnnotation(ExcelTitle.class) != null && !item.getAnnotation(ExcelTitle.class).ignore())
                        .sorted(Comparator.comparingInt(item -> {
                            ExcelTitle excelTitle = item.getAnnotation(ExcelTitle.class);
                            return excelTitle.order();
                        })).collect(Collectors.toList());

                for (int i = 0; i < fieldList.size(); i++) {
                    //根据title的值对应的值
                    SXSSFCell cell = row.createCell(i);
                    Field field = fieldList.get(i);
                    cell.setCellStyle(initCellStyle(wb, field));
                    field.setAccessible(true);
                    cell.setCellValue(dealValue(field, dataObject));
                }
                dataStartLine++;
            }
        } catch (Exception e){
            LOGGER.error("生成数据异常", e);
        }

    }

key:value数据解析

private String dealValue(Field field, Object dataObject) throws IllegalAccessException {
        ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);

        Object value = field.get(dataObject);
        if(value == null){
            return null;
        }
        if(excelProperty != null && StringUtils.isNotEmpty(excelProperty.textKv())){
            String[] kvs = excelProperty.textKv().split(";");

            Map<String, String> map = new HashMap<>();
            for(String str: kvs){
                map.put(str.split("=")[0], str.split("=")[1]);
            }
            return map.get(value.toString());
        }
        return value.toString();
    }

导出设置:

/**
     * 导出excel数据
     *
     * @param response 亲求返回
     * @param list     每个sheet页的数据,一个elist表示一个sheet页
     * @param fileName 导出的名称
     * @return 结果
     */
    public boolean creatExcel(HttpServletResponse response, List<ExcelSheetVo> list, String fileName) {
        SXSSFWorkbook wb = creatBook(list);
        //导出数据
        try {
            //设置Http响应头告诉浏览器下载这个附件
            response.reset();
            response.setCharacterEncoding("utf-8");
            response.setContentType("application/vnd.ms-excel");
            //名称要从新进行 ISO8859-1 编码否则会文件名称会乱码
            response.setHeader("Content-Disposition", "attachment;Filename=" + encodeFileName(fileName) + ".xlsx");
            OutputStream outputStream = response.getOutputStream();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            wb.write(baos);
            outputStream.write(baos.toByteArray());
            baos.flush();
            baos.close();
            outputStream.close();
        } catch (Exception ex) {
            LOGGER.error("导出excel失败", ex);
        }
        return true;
    }

  private String encodeFileName(String fileName) {
        try {
            //fileName = java.net.URLEncoder.encode(fileName, "UTF-8");
            fileName = URLDecoder.decode(fileName, "UTF-8");
            return new String(fileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
        } catch (UnsupportedEncodingException e) {
            return "未命名";
        }
    }

测试

定义数据实体

数据标题和数据定义

@Data
public class TestExcelVo {

    @ExcelTitle(titleName = "名称", titleColor = HSSFColor.HSSFColorPredefined.BLUE, borderStyle = BorderStyle.HAIR, titleSize = 12, titleBack = IndexedColors.YELLOW)
    @ExcelProperty()
    private String name;

    @ExcelTitle(titleName = "年龄", titleColor = HSSFColor.HSSFColorPredefined.GREEN, borderStyle = BorderStyle.HAIR, titleBack = IndexedColors.RED)
    @ExcelProperty(textType = "0", textColor = HSSFColor.HSSFColorPredefined.RED)
    private Integer age;

    @ExcelTitle(titleName = "性别", titleColor = HSSFColor.HSSFColorPredefined.BLUE, titleSize = 12, titleBack = IndexedColors.GREEN)
    @ExcelProperty(textKv = "0=男;1=女", textBack = IndexedColors.BROWN)
    private Integer sex;

    @ExcelTitle(titleName = "描述", titleColor = HSSFColor.HSSFColorPredefined.BLUE, titleBack = IndexedColors.GREEN)
    @ExcelProperty(boldFont = true, textSize = 18)
    private String des;
}

sheet的数据定义 

@Data
public class ExcelSheetVo<T> {

    /**
     * 每一个sheet页得名称
     */
    private String sheetName;

    /**
     * 每个sheet里面得数据
     * 其中Object中得注解必须时包含 @ExcelTitle 和 @ExcelProperties
     */
    private List<T> sheetData;


    /**
     * 数据对象得类型
     */
    private Class dataClass;
}

测试代码

@RestController
@RequestMapping(value = "exceExport")
public class ExcelExportController {

    @GetMapping("testExport")
    public void testExport(HttpServletResponse response){

        String fileName = "测试sheet";
        List<ExcelSheetVo> sheetVoList = new ArrayList<>();

        for(int i = 0; i < 3; i++){
            sheetVoList.add(createSheetData(i));
        }

        new ExcelCreateUtil().creatExcel(response, sheetVoList, fileName);
    }

    private ExcelSheetVo<TestExcelVo> createSheetData(int index){
        ExcelSheetVo<TestExcelVo> excelSheetVo = new ExcelSheetVo<>();
        excelSheetVo.setDataClass(TestExcelVo.class);
        excelSheetVo.setSheetName("sheet" + index);

        List<TestExcelVo> testExcelVos = createTestExcelVo(new Random().nextInt(30) + 10, "sheet" + index);
        excelSheetVo.setSheetData(testExcelVos);
        return excelSheetVo;
    }


    private List<TestExcelVo> createTestExcelVo(int size, String sheetName){

        List<TestExcelVo> testExcelVos = new ArrayList<>();
        for(int i = 0; i < size; i++){
            TestExcelVo testExcelVo = new TestExcelVo();
            testExcelVo.setName(sheetName + "-" + i);
            testExcelVo.setAge(i);
            testExcelVo.setSex(i % 2);
            testExcelVo.setDes("哈哈哈哈哈" + i);
            testExcelVos.add(testExcelVo);
        }
        return testExcelVos;
    }
}

结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1947006.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【LeetCode】201. 数字范围按位与

1. 题目 2. 分析 这题挺难想的&#xff0c;我到现在还没想明白&#xff0c;为啥只用左区间和右区间就能找到目标值了&#xff0c;而不用挨个做与操作&#xff1f; 3. 代码 class Solution:def rangeBitwiseAnd(self, left: int, right: int) -> int:left_bin bin(left).…

五. TensorRT API的基本使用-TensorRT-network-structure

目录 前言0. 简述1. 案例运行2. 代码分析2.1 main.cpp2.2 model.cpp 总结下载链接参考 前言 自动驾驶之心推出的 《CUDA与TensorRT部署实战课程》&#xff0c;链接。记录下个人学习笔记&#xff0c;仅供自己参考 本次课程我们来学习课程第五章—TensorRT API 的基本使用&#x…

java面向对象进阶进阶篇--《接口和接口与抽象类综合案例》(附带全套源代码)

个人主页→VON 收录专栏→java从入门到起飞 抽象类→抽象类和抽象方法 目录 一、初识接口 特点和用途 示例&#xff1a; Animal类 Dog类 Frog类 Rabbit类 Swim接口 text测试类 结果展示&#xff1a; 二、接口的细节 接口中的成员特点&#xff1a; 成员特点与接口的关…

【通信模块】WiFi&Bluetooth简介与对比

学习云里物里科技文章及结合CSDN优秀作者Edison Tao总结笔记&#xff0c;侵权联删&#xff01; 云里物里科技&#xff1a; https://www.minewtech.com/news/industry-2019-01-25-01.html CSDN&#xff1a; https://blog.csdn.net/taotongning/article/details/95215927 WIFI…

EXCEL 排名(RANK,COUNTIFS)

1.单列排序 需求描述&#xff1a;如有下面表格&#xff0c;需要按笔试成绩整体排名。 解决步骤&#xff1a; 我们使用RANK函数即可实现单列整体排名。 Number 选择第一列。 Ref 选择这一整列&#xff08;CtrlShift向下箭头、再按F4&#xff09;。 "确定"即可计算…

C++ | Leetcode C++题解之第284题窥视迭代器

题目&#xff1a; 题解&#xff1a; template <class T> class PeekingIterator : public Iterator<T> { public:PeekingIterator(const vector<T>& nums) : Iterator<T>(nums) {flag Iterator<T>::hasNext();if (flag) {nextElement Ite…

AUTOSAR从入门到精通-CAN-FD

目录 几个高频面试题目 CAN与CAN FD的区别是什么? 一、CAN与CAN FD的概念 二、CAN与CANFD的比较 三、CAN与CANFD的优劣势 为何CANFD还不能大面积取代CAN总线? 算法原理 什么是CAN FD CAN FD的特点 为什么会出现CAN FD? CAN FD和CAN总线协议帧异同 Can FD报文讲解…

调用python-docx 提示出错

&#x1f3c6;本文收录于《CSDN问答解答》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&…

【Django】 读取excel文件并在前端以网页形式显示-安装使用Pandas

文章目录 安装pandas写views写urls安装openpyxl重新调试 安装pandas Pandas是一个基于NumPy的Python数据分析库&#xff0c;可以从各种文件格式如CSV、JSON、SQL、Excel等导入数据&#xff0c;并支持多种数据运算操作&#xff0c;如归并、再成形、选择等。 更换pip源 pip co…

C#开发的全屏图片切换效果应用 - 开源研究系列文章 - 个人小作品

这天无聊&#xff0c;想到上次开发的图片显示软件《 PhotoNet看图软件 》&#xff0c;然后想到开发一个全屏图片切换效果的应用&#xff0c;类似于屏幕保护程序&#xff0c;于是就写了此博文。这个应用比较简单&#xff0c;主要是全屏切换换图片效果的问题。 1、 项目目录&…

Boost搜索引擎项目相关介绍

Boost搜索引擎相关介绍&#xff1a; 首先&#xff0c;Boost库不具备搜索条件&#xff0c;所以我们这个项目借此实现搜索功能。 项目的核心就是以用户搜索的相关内容在目标数据中进行查找。 首先&#xff0c;我们面临的第一大难题就是目标数据&#xff0c;在这里目标数据就是Boo…

气膜羽毛球馆的维护和运营成本解析—轻空间

随着人们对健康生活方式的追求不断增加&#xff0c;羽毛球这项运动也愈发受到欢迎。然而&#xff0c;传统的羽毛球馆往往存在建设周期长、成本高、维护复杂等问题。气膜羽毛球馆作为一种新型的运动场馆解决方案&#xff0c;因其快速搭建、环保节能、舒适环境等优势而逐渐被广泛…

整合StarRocks更新表全部知识点

总结StarRocks更新表的全部内容的集合&#xff08;V3.2版本&#xff09; 一、基本功能 聚合函数replace的聚合表主键被主键表替代采用Merge-On-Read的策略&#xff0c;读取时需要在线Merge多个版本的数据文件&#xff0c;谓词和索引无法下推至底层数据&#xff0c;会严重影响…

开源AI智能名片O2O商城小程序在私域流量营销中的应用与实践

摘要 在数字化时代&#xff0c;私域流量营销已成为企业构建长期客户关系、实现可持续增长的关键策略。本文深入探讨了开源AI智能名片O2O商城小程序如何以其独特的智能化、个性化及高效性&#xff0c;在私域流量营销领域发挥重要作用。通过详细分析其在扩大公域影响力、打造微信…

c生万物系列(封装)

为了对c语言进行封装&#xff0c;笔者参考了lw_oopc等开源库&#xff0c;决定使用宏对结构体进行封装。 先说一下大致思想&#xff1a;通过宏&#xff0c;结构体和文件来实现封装。 大概步骤&#xff1a;抽象出类-> 使用lw_oopc库进行封装->定义接口封装底层实现 ->…

SQL labs-SQL注入(六)

本文仅作为学习参考使用&#xff0c;本文作者对任何使用本文进行渗透攻击破坏不负任何责任。 本文为SQL labs-SQL注入&#xff08;四&#xff09;的续作&#xff0c;依据sqllabs靶场&#xff0c;详细讲解手工post注入。 一&#xff0c;观察页面&#xff08;SQL labs-less11&a…

网站基本布局CSS

代码 <!DOCTYPE html> <html> <head><meta charset"utf-8"><meta name"viewport" content"widthdevice-width, initial-scale1"><title></title><style type"text/css">body {margi…

FullCalendar日历组件集成系列5——高级配置1

高级配置 调整单元格高度 默认情况下&#xff0c;单元格高度会自动扩展&#xff0c;如下图所示&#xff0c;留下不小的空白&#xff0c;既浪费空间又不美观。解决方式就是为高度height属性指定值auto&#xff0c;如下&#xff1a; // 高度自动调整 height: auto效果如下&…

设计模式 之 —— 单例模式

目录 什么是单例模式&#xff1f; 定义 单例模式的主要特点 单例模式的几种设计模式 1.懒汉式&#xff1a;线程不安全 2.懒汉式&#xff1a;线程安全 3.饿汉式 4.双重校验锁 单例模式的优缺点 优点&#xff1a; 缺点&#xff1a; 适用场景&#xff1a; 什么是单例模…

Golang | Leetcode Golang题解之第283题移动零

题目&#xff1a; 题解&#xff1a; func moveZeroes(nums []int) {left, right, n : 0, 0, len(nums)for right < n {if nums[right] ! 0 {nums[left], nums[right] nums[right], nums[left]left}right} }