Java中List、Set、数组、Map之间相互转换

1、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);

2、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]);

3、List、Set、数组和流(Stream)的互转

Stream(流)是Jdk8开始引入的一个抽象概念,它并不是实体的数据结构,不保存对象,但是它可以在让要处理的元素在管道中传输,在管道的各个节点上进行筛选、排序、聚合等处理。

打个比方,可以把List等实体的数据结构想象成水桶,那么Stream就是水管,可以把水桶中的水抽到水管中进行处理,但是处理过后一定要再用一个容器去容纳这些水。

4、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());

5、数组和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();

6、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));

7、数据分组及后续处理

想要对数据进行分组,我们可以先把集合转成流,再调用流的归并操作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)));

8、数据的筛选、按多个条件排序

1)筛选——filter

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

Double average = students.stream()
            .filter(s -> s.getGender() == 1) // 按条件过滤
            .collect(Collectors.averagingDouble(Student::getScore));

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

2)排序——sort

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

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

3)按字段排序—Comparator.comparing

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

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

4)多字段排序—thenComparing方法

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

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

5)倒序—Comparator的reversed方法

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

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

9、总结

  • 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()方法翻转比较器的排序规则。

发表评论