百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

提高工作效率的工具类,赶快收藏起来

toyiye 2024-07-03 02:07 15 浏览 0 评论

本篇文章主要是为了记录工作中经常使用的一些工具,以及解决常见问题的一些常用套路。

一、Java实现List分隔成子List

在工作中经常遇到需要将数组分割成多个子数组,然后进行批量处理的需求。那有没有比较优雅的实现呢?

经过多次实践,总结出 ListUtils.partition 和 Lists.partition 两种较好实现 。下面对这两种实现分别进行说明。

1.1 ListUtils.partition 方法

1.1.1 引入依赖

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.4</version>
</dependency>

1.1.2 代码演示

public static void main(String[] args) {
    //初始化数组
    List<Integer> parList = new ArrayList<>();
    IntStream.range(0, 30).forEach(parList::add);
    //分割成子数组
    List<List<Integer>> subList = ListUtils.partition(parList, 10);
    //遍历子数组
    subList.forEach(list -> {
        System.out.println(String.format("subList size:%s", list.size()));
        System.out.println(String.format("list:%s", list.toString()));
    });
}

1.1.3 输出结果

1.2 Lists.partition 方法

1.2.1 引入依赖

<dependency>	
  <groupId>com.google.guava</groupId>	
  <artifactId>guava</artifactId>	
  <version>29.0-jre</version>
</dependency>

1.2.2 代码演示

public static void main(String[] args) {
    //初始化数组
    List<Integer> parList = new ArrayList<>();
    IntStream.range(0, 30).forEach(parList::add);
 
    //分割成子数组
    List<List<Integer>> subList = Lists.partition(parList, 10);
 
    //遍历子数组
    subList.forEach(list -> {
        System.out.println(String.format("subList size:%s", list.size()));
        System.out.println(String.format("list:%s", list.toString()));
    });
}

1.2.3 输出结果

1.3 源码分析

1.3.1 ListUtils.partition 源码分析

public static <T> List<List<T>> partition(List<T> list, int size) {
    if (list == null) {
        throw new NullPointerException("List must not be null");
    } else if (size <= 0) {
        throw new IllegalArgumentException("Size must be greater than 0");
    } else {
       return new ListUtils.Partition(list, size);
    }
}

最终 ListUtils.partition 调用 ListUtils.Partition 方法来处理。

ListUtils.Partition 源码如下:

private static class Partition<T> extends AbstractList<List<T>>
    private final List<T> list;
    private final int size;

    private Partition(List<T> list, int size) {
        this.list = list
        this.size = size
    }

    public List<T> get(int index) {
        int listSize = this.size();
        ir (index < 0) {
            throw new IndexOutOfBoundsException("Index " + index + " must not be negative");
        } else ir (index >= listSize) {
            throw new IndexOutOfBoundsException("Index " + index + " must be less than size " + listSize);
        } else {
            int start = index * this.size;
            int end = Math.min(start + this.size, this.list.size());
            return this.list.subList(start, end);
        }
    }

    public int size() {
        return (int)Math.ceil((double)this.list.size() / (double)this.size)
    }

    public boolean isEmpty() { 
        return this.list.isEmpty();
    }
}

Partition 类作为 ListUtils 静态内部类继承 AbstractList 类。重写了 get 和 size方法。

1.3.2 Lists.partition 源码分析

public static <T> List<List<T>> partition(List<T> list, int size) {
      checkNorNull (list) ;
      checkArgumenr(size > 0) ;
      return (list instanceof RandomAccess)
      ? new RandomAccessPartition<>(list, size)
      : new Partition<>(list, size);
}

Lists.partition 方法最终会调用 new Partition<>(list, size)。

Partition 类源码如下:

Partition 类作为 Lists 静态内部类继承 AbstractList 类。重写了 get 、 size、isEmpty 方法。

二、Future 多线程处理任务
你是否遇到过这种场景,有大量数据需要进行处理,比如几百万、几千万,甚至上亿的数据?

如果使用单线程来处理大量的数据,耗时将会很长,效率太低。你也许会说,这简单啊,用多线程啊。是的,多线程相当于多个单线程同时处理,确实快很多。

那么如何使用多线程来处理?

你会说用线程池哈。哈哈,线程池确实很常用,使用线程池确实能达到预期效果。

如果需要获取线程处理得到的结果,又该如何呢?

Runnable 接口,它的方法没有返回值,不符合要求。

Callable 接口,和 Runnable 接口比,多了一个返回值,符合要求。

2.1 定义线程池

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 
@Configuration
public class TaskThreadPoolConfig {
 
    private int corePoolSize = 8;
    private int maxPoolSize = 8;
    private int queueCapacity = 1000;
 
    @Bean(name = "taskExecutor")
    public ThreadPoolTaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        return executor;
    }
}

2.2 Future 实践应用

import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.stream.IntStream;
import org.springframework.stereotype.Service;
 
@Service
public class TaskExecutor {
    @Resource(name = "taskExecutor")
    ThreadPoolTaskExecutor threadPoolTaskExecutor;
 
    public List<Integer> futureList() throws ExecutionException, InterruptedException {
        //初始化任务数组
        List<Integer> initList = new ArrayList<>();
        IntStream.range(0, 30).forEach(initList::add);
 
        List<Future> futureList = new ArrayList<>();
        //多线程执行
        initList.forEach(id -> {
            Future<Integer> future = threadPoolTaskExecutor.submit(() -> getUserId(id));
            //将future写入list
            futureList.add(future);
        });
        
        //获取多线程执行结果
        List<Integer> resultList = new ArrayList<>();
        for (Future<Integer> future : futureList) {
            resultList.add(future.get());
        }
        
        return resultList;
    }
    
    public Integer getUserId(Integer i) {
        return i;
    }
}

从上面的例子可以看到:

1 当我们提交一个 Callable 任务后,我们会同时获得一个 Future 对象,

2 主线程某个时刻调用 Future 对象的 get() 方法,就可以获得异步执行的结果。

3 在调用 get()时,如果异步任务已经完成,我们就直接获得结果;如果异步任务还没有完成,那么 get()会阻塞,直到任务完成后才返回结果。

综上所述:我们可以看到,使用 Future 对象的 get() 方法,可能会阻塞,系统的吞吐率很难提高。CompletableFuture 是 Future 的升级版,这里不做深入研究。


三 内存分页

在实际工作中,经常会遇到这样一种场景,需要展示列表,出于对系统性能的考虑,需要对列表进行分页渲染。

3.1 分页代码

import org.apache.commons.lang3.tuple.Pair;
 
public static void main(String[] args) {
    List<Integer> list =  Arrays.asList(1, 2, 3, 4, 5, 6);
    Pair<Integer, List<Integer>> pair = page(list, 0, 2);
    System.out.println(pair.getLeft());
    System.out.println(pair.getRight());
}
 
public static Pair<Integer, List<Integer>> page(List<Integer> list, Integer offset, Integer limit) {
    // offset偏移量与total比较
    int total = CollectionUtils.isNotEmpty(list) ? list.size() : 0;
    if (offset > total) {
        return Pair.of(total, Lists.newArrayList());
    }
 
    // 计算开始偏移量和最终偏移量
    int end = offset + limit;
    // 最终偏移量不能大于total
    end = Math.min(end, total);
    // 分页取子集
    List<Integer> result = list.subList(offset, end);
    return Pair.of(total, result);
}

3.2 执行结果

四、Java8 stream 实现多字段排序

工作中经常会遇到对字段进行排序的操作,如果不是需要单独定义排序规则的话,可以使用 stream 提供的排序 API,支持多字段排序,简单易用。

4.1 代码实现

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
 
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
 
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {
  
    private Long id;
    private String name;
    private Integer age;
 
    public static void main(String[] args) {
        List<User> userList = new ArrayList<>();
        userList.add(User.builder().id(10L).name("zs").age(11).build());
        userList.add(User.builder().id(10L).name("zs").age(10).build());
        userList.add(User.builder().id(5L).name("ls").age(9).build());
        userList.add(User.builder().id(5L).name("mz").age(9).build());
        userList.add(User.builder().id(19L).name("ww").age(10).build());
        System.out.println("排序之前:");
        for (User u:userList) {
            System.out.println(u);
        }
 
        // 假设我们以id降序、name降序、age升序对数组进行排序
        List<User> sort = userList.stream().sorted(Comparator.comparing(User::getId, Comparator.reverseOrder())
                .thenComparing(User::getName, Comparator.reverseOrder())
                .thenComparing(User::getAge, Comparator.naturalOrder())).collect(Collectors.toList());
        System.out.println("排序之后:");
        for (User u : sort) {
            System.out.println(u);
        }
    }
}

4.2 运行结果

五、Search Scroll API

主要解决从索引里面取大数据量的场景,Scroll API 分批取数据的方式提高了查询效率。

5.1、Scroll API 代码

// 设置索引
SearchRequest searchRequest = new SearchRequest("posts");
 
// 设置scroll生命周期
final Scroll scroll = new Scroll(TimeValue.timeValueMinutes(1L));
searchRequest.scroll(scroll);
 
// 设置搜索条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(matchQuery("title", "Elasticsearch"));
// 设置单次查询数目
searchSourceBuilder.size(size); 
searchRequest.source(searchSourceBuilder);
 
 
// 第一次查询获取结果
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT); 
String scrollId = searchResponse.getScrollId();
SearchHit[] searchHits = searchResponse.getHits().getHits();
 
while (searchHits != null && searchHits.length > 0) { 
    // 构造scroll请求,查询下一页
    SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId); 
    scrollRequest.scroll(scroll);
    searchResponse = client.scroll(scrollRequest, RequestOptions.DEFAULT);
    scrollId = searchResponse.getScrollId();
    searchHits = searchResponse.getHits().getHits();
}
 
// 从上下文清除数据
ClearScrollRequest clearScrollRequest = new ClearScrollRequest(); 
clearScrollRequest.addScrollId(scrollId);
ClearScrollResponse clearScrollResponse = client.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);
boolean succeeded = clearScrollResponse.isSucceeded();

5.2、Scroll API 流程梳理

1. 通过发送初始 SearchRequest 初始化搜索上下文。

2. 通过在循环中调用 Search Scroll api 检索所有搜索命中,直到不返回任何文档。

3. 处理返回的搜索结果。

4. 创建一个新的 SearchScrollRequest,其中包含最后返回的滚动标识符和滚动间隔。

5. 滚动完成后清除滚动上下文。

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码