吉森的技术小站 吉森的技术小站
首页
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

吉森

Fuel your ambition
首页
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Java基础

    • 就从Java8开始吧(一)lambda表达式详解
    • 就从java8开始吧(二)lambda表达式和方法引用
    • 就从Java8开始吧(三)说一说Stream
    • 就从Java8开始吧(四)唠一唠Optional
    • 提高开发效率的奇技淫巧(一)lombok
    • maven概览
    • 就从Java8开始吧(五)新日期和时间API
    • 就从Java8开始吧(六)接口默认方法和静态方法
    • 提高开发效率的奇技(二)Intellij IDEA的进阶应用
    • 深入浅出java web(一):从根说起——servlet
    • 深入浅出java web (二):简述http协议
    • Java中equals()与==的区别详解
    • Java代理模式详解
    • Java中System.getProperty("user.dir")详解
    • Java格式工厂(一)——什么是二进制文件?
    • Java菜谱(二)——怎么求男学生的平均分?
    • Java菜谱(三)——常用数据结构转换及处理
      • 概述
      • 快速参考
      • 基础数据结构转换
        • List和Set互转
        • List/Set和Array互转
      • Stream流操作
        • List/Set和Stream互转
        • Array和Stream互转
        • List转为Map
      • 数据处理
        • 数据分组及后续处理
        • 数据筛选和排序
        • filter
        • sort
        • Comparator.comparing
        • Comparator的thenComparing方法
        • Comparator的reversed方法
      • 总结
    • Java菜谱(四)——怎么将10万条数据导出到excel?
    • Java菜谱(五)——怎么把字符串列表合并为一个字符串?
    • 函数式编程入门——拥抱函数式时代
    • Java Stream findFirst方法的空指针陷阱详解
  • Spring框架

  • 第三方库

  • Java
  • Java基础
吉森
2021-04-21
目录

Java菜谱(三)——常用数据结构转换及处理

0 人阅读

# 概述

今天的菜品是这样的:

List、Set、数组、Map这些常用的数据结构之间是怎么转换的?

# 快速参考

  • List和Set互转用构造器
  • List和Set转数组用toArray()方法
  • 数组转List和Set用构造器嵌套Arrays.asList()方法
  • List转Map,先将List转为Stream,再利用Stream的collect(Collectors.toMap())方法转为Map
  • List/Set转Stream用stream()方法
  • 数组转Stream用Arrays.stream()方法
  • 可以用Comparator.comparing()方法构造比较器,可以用thenComparing()方法构造多字段排序的构造器,可以用reversed()方法翻转比较器的排序规则。

# 基础数据结构转换

# List和Set互转

List和Set之间的相互转换主要靠List和Set的有参构造器。这种方法同样适用于List或Set不同实现类之间的互相转换。

# List/Set和Array互转

数组转List/Set时,首先可以利用Arrays.asList方法转换为List,这时转成的List对象是一个名为ArrayList的内部类的对象,它是原数组的视图,有很多操作不支持。因此我们需要再利用上一节中的方法用List/Set的构造器再包一层。List/Set转为数组时,可以直接用数据的toArray方法,由于Java中泛型的限制,toArray方法需要传一个数组作为参数,这个数组通常传一个新建的相应类型的0元素数组即可。

Integer[] array = {1, 2, 3, 4, 5};

// array -> list       
List<Integer> listFromArray = new ArrayList<>(Arrays.asList(array));
System.out.println(listFromArray);

// list -> array
Integer[] arrayFromList = listFromArray.toArray(new Integer[0]);
System.out.println(Arrays.toString(arrayFromList));

// list -> set
Set<Integer> setFromList = new HashSet<>(listFromArray);
System.out.println(setFromList);

// set -> list
List<Integer> listFromSet = new ArrayList<>(setFromList);
System.out.println(listFromSet);

// set -> array
Integer[] arrayFromSet = setFromList.toArray(new Integer[0]);
System.out.println(Arrays.toString(arrayFromSet));

// array -> set
Set<Integer> setFromArray = new HashSet<>(Arrays.asList(array));
System.out.println(setFromArray);
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

# Stream流操作

Stream(流)是Jdk8开始引入的一个抽象概念,它并不是实体的数据结构,不保存对象,但是它可以在让要处理的元素在管道中传输,在管道的各个节点上进行筛选、排序、聚合等处理。可以把List等数据结构想象成水桶,Stream就是水管,我们可以把水桶中的水抽到水管中进行处理,但是处理过后一定要再用一个容器去容纳这些水。

# List/Set和Stream互转

List/Set转为Stream,可以利用这些类本身的stream()方法。Stream转为List/Set时,可以利用Stream的collect方法,将Collectors.toList()或Collectors.toSet()作为参数。

# Array和Stream互转

Array转为Stream,可以使用Arrays.Stream方法或者Stream.of方法,这些方法的参数都是泛型不定长参数,可以将数组作为参数传入。但是需注意,如果要转换的数组是基本数据类型(int、long和double),那么必须使用Arrays.Stream方法,因为该方法为基本数据类型设定了重载方法。如果使用Stream.of方法,整个数组会被当成一个元素,从而将数组转换为了包含一个元素的数组的流。另外注意,除了int、long和double外,其他基本数据类型没有对应的基本流。

// array -> stream
Integer[] array = {1, 2, 3, 4, 5};
int[] intArray = {1, 2, 3, 4, 5};

// 1. Stream.of没有为int[]单独设置重载方法,因此整个int[]对象会被当做一个参数
Stream<Integer> stream1 = Stream.of(array);
Stream<int[]> stream2 = Stream.of(intArray);

// 2. Arrays.stream为int[]单独设置了重载方法,因此会被转换成基本数据流IntStream(流里存的元素是int类型)
Stream<Integer> stream3 = Arrays.stream(array);
IntStream intStream = Arrays.stream(intArray);

// stream -> array
Integer[] arrayFromStream = stream1.toArray(Integer[]::new);
int[] intArrayFromStream = intStream.toArray();

// list -> stream
List<Integer> listFromArray = new ArrayList<>(Arrays.asList(array));
Stream<Integer> streamFromList = listFromArray.stream();

// stream -> list
List<Integer> listFromStream = streamFromList.collect(Collectors.toList());

// set -> stream
Set<Integer> setFromArray = new HashSet<>(Arrays.asList(array));
Stream<Integer> streamFromSet = setFromArray.stream();

// stream -> set
Set<Integer> setFromStream = streamFromSet.collect(Collectors.toSet());
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

# List转为Map

想要将List转为Map,我们可以先把集合转成流,再调用流的归并操作collect。collect方法的数据通过调用Collectors.toMap方法获取,该方法的两个参数分别传根据List中元素生成Map的key和value的策略。

List<Student> students = new ArrayList<>();
students.add(student);
// 将list转为学生ID和学生对象的映射表
Map<Integer, Student> studentMap = students.stream()
    .collect(Collectors.toMap(Student::getId, s->s));
1
2
3
4
5

# 数据处理

# 数据分组及后续处理

想要对数据进行分组,我们可以先把集合转成流,再调用流的归并操作collect。collect方法的数据通过调用Collectors.groupingBy方法获取,groupingBy方法可以传一到两个参数,第一个参数为分组的依据,如果传第二个参数,则它代表分组后的后续(downstream)操作。以求学生平均分为例:

List<Student> students = new ArrayList<>();

// 求男/女学生的平均分
Map<Integer, Double> averageScoreMap = students.stream()
        .collect(Collectors.groupingBy(Student::getGender, Collectors.averagingDouble(Student::getScore)));
1
2
3
4
5

# 数据筛选和排序

# filter

如果我们只需要获得男学生的平均分,就不需要进行分组操作了,可以直接使用filter方法进行过滤操作,如下:

final Double average = students.stream()
            .filter(s -> s.getGender() == 1)
            .collect(Collectors.averagingDouble(Student::getScore));
1
2
3

过滤操作传入的参数是一个Predicate类型的对象,它代表元素保留下来的条件。

# sort

如果需要对集合进行排序,我们可以使用List的sort方法,也可以使用Stream的sorted方法。这些方法要求我们传一个Comparator对象,作为排序的依据。如:

students.sort((s1, s2) -> {
        double diff = s1.getScore() - s2.getScore();
        return diff > 0 ? 1 : diff == 0 ? 0 : -1;
    });
1
2
3
4

# Comparator.comparing

如果我们是根据类中的某个字段对某个类的对象进行排序的话,我们可以使用Comparator.comparing这个静态方法构造Comparator对象。因此,上述代码可以简化为:

students.sort(Comparator.comparing(Student::getScore));
1

# Comparator的thenComparing方法

有时我们需要对多个字段进行排序,这时我们可以利用Comparator对象的thenComparing方法构造一个用于多字段排序的比较器,方法中需要传一个新的比较器或比较策略。如对学生先按班级再按分数进行排序:

students.sort(Comparator.comparing(Student::getClassNumber)
.thenComparing(Student::getScore));
1
2

# Comparator的reversed方法

Comparator.comparing默认是按自然排序,即由小到大排序。有的时候我们需要按从大到小的顺序进行排序,此时我们可以调用Comparator的reversed方法,如对学生分数按由高到低排序:

students.sort(Comparator
.comparing(Student::getScore)
.reversed());
1
2
3

# 总结

本文介绍了常用数据结构相互转换的方法以及一些数据的常见处理方式。今天的文章就到这里,有什么想要聊的话题,欢迎留言。喜欢文章的同学也可以鼓励我一下哦~

编辑 (opens new window)
上次更新: 2025/08/08, 17:43:26
Java菜谱(二)——怎么求男学生的平均分?
Java菜谱(四)——怎么将10万条数据导出到excel?

← Java菜谱(二)——怎么求男学生的平均分? Java菜谱(四)——怎么将10万条数据导出到excel?→

最近更新
01
怎么写好技术文章?
08-25
02
CommonJS与ES模块:新手完全指南
08-21
03
Java Stream findFirst方法的空指针陷阱详解
08-14
更多文章>
Theme by Vdoing | Copyright © 2024-2025 吉森 | MIT License | 吉ICP备17006653号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式