大家好,我是吉森。
今天的菜品是这样的:
List、Set、数组、Map这些常用的数据结构之间怎么相互转换?
下面,让我们一起来解决一下这个问题。
List和Set互转List和Set之间的相互转换主要使用List和Set的有参构造器。这种方法同样适用于List或Set不同实现类之间的互相转换。
// list -> set
Set<Integer> setFromList = new HashSet<>(list);
// set -> list
List<Integer> listFromSet = new ArrayList<>(set);
// arrayList -> linkedList
List<Integer> linkedList = new LinkedList<>(arrayList);
// hashSet -> treeSet
Set<Integer> treeSet = new TreeSet<>(hashSet);
List/Set和数组互转
数组转换为List/Set时,第一步可以先利用Arrays.asList方法转换为List。这时转成的List对象是一个名为ArrayList的内部类的对象,它是原数组的视图,不支持增删操作。因此,我们需要再利用上一节中的方法用List/Set的构造器将上一步得到的List再包一层。
List/Set转为数组时,可以直接用数据的toArray方法,由于Java中泛型无法在运行时获取到实际类型,toArray方法需要传一个数组作为参数,这个数组传一个相应类型的长度为0的数组即可。
// array -> list
List<Integer> listFromArray = new ArrayList<>(Arrays.asList(array));
// array -> set
Set<Integer> setFromArray = new HashSet<>(Arrays.asList(array));
// list -> array,注意传入了一个长度为0的数组来指名类型
Integer[] arrayFromList = list.toArray(new Integer[0]);
// set -> array,注意传入了一个长度为0的数组来指名类型
Integer[] arrayFromSet = set.toArray(new Integer[0]);
List、Set、数组和流(Stream)的互转
Stream(流)是Jdk8开始引入的一个抽象概念,它并不是实体的数据结构,不保存对象,但是它可以在让要处理的元素在管道中传输,在管道的各个节点上进行筛选、排序、聚合等处理。
打个比方,可以把List等实体的数据结构想象成水桶,那么Stream就是水管,我们可以把水桶中的水抽到水管中进行处理,但是处理过后一定要再用一个容器去容纳这些水。
List/Set和Stream互转List/Set转为Stream,可以利用这些类本身的stream()方法。
Stream转为List/Set时,可以利用Stream的collect()方法,将Collectors.toList()或Collectors.toSet()作为参数。
// list -> stream
Stream<Integer> streamFromList = list.stream();
// stream -> list
List<Integer> listFromStream = stream.collect(Collectors.toList());
// set -> stream
Stream<Integer> streamFromSet = set.stream();
// stream -> set
Set<Integer> setFromStream = stream.collect(Collectors.toSet());
数组和Stream互转
数组转为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转为Map
想要将List转为Map,我们可以先把集合转成流,再调用流的归并操作collect()方法。collect()方法的参数通过调用Collectors.toMap()方法获取,该方法的两个参数为两个Function类型对象,分别代表根据List中元素生成Map的key和value的策略。
// 利用jdk9的api创建一个学生的list
List<Student> students = List.of(student0, student1, student2);
// 将list转为学生ID和学生对象的映射表
Map<Integer, Student> studentMap = students.stream()
.collect(Collectors.toMap(Student::getId, s->s));
数据分组及后续处理
想要对数据进行分组,我们可以先把集合转成流,再调用流的归并操作collect()方法。collect()方法的参数通过调用Collectors.groupingBy()方法获取,groupingBy方法可以传一到两个参数,第一个参数为分组的依据,如果传第二个参数,则它代表分组后的后续(downstream)操作。以求学生平均分为例:
List<Student> students = List.of(student0, student1, student2);
// 求男/女学生的平均分
Map<Integer, Double> averageScoreMap = students.stream()
.collect(Collectors.groupingBy(Student::getGender, Collectors.averagingDouble(Student::getScore)));
数据的筛选、按多个条件排序筛选——filter
如果我们只需要获得男学生的平均分,就不需要进行分组操作了,可以直接使用filter方法进行过滤操作,如下:
Double average = students.stream()
.filter(s -> s.getGender() == 1) // 按条件过滤
.collect(Collectors.averagingDouble(Student::getScore));
过滤操作传入的参数是一个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;
});
按字段排序—Comparator.comparing
如果我们是根据类中的某个字段对某个类的对象进行排序的话,我们可以使用Comparator.comparing这个静态方法构造Comparator对象。因此,上述代码可以简化为:
students.sort(Comparator.comparing(Student::getScore));
多字段排序—thenComparing方法
有时我们需要对多个字段进行排序,这时我们可以利用Comparator对象的thenComparing方法构造一个用于多字段排序的比较器,方法中需要传一个新的比较器或比较策略。如对学生先按班级再按分数进行排序:
students.sort(Comparator.comparing(Student::getClassNumber)
.thenComparing(Student::getScore));
倒序—Comparator的reversed方法
Comparator.comparing默认是按自然排序,即由小到大排序。有的时候我们需要按从大到小的顺序进行排序,此时我们可以调用Comparator的reversed方法,如对学生分数按由高到低排序:
students.sort(Comparator
.comparing(Student::getScore)
.reversed());
总结
- 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()方法翻转比较器的排序规则。
本文介绍了常用数据结构相互转换的方法以及一些数据的常见处理方式。今天的文章就到这里了,有什么想要聊的话题,欢迎留言。喜欢本文的同学也可以点赞转发支持一下哦~