### java8之collector详解,以及结合toMap,sorted,groupingBy使用例子

100 篇文章 14 订阅

最近接触到java8的collector功能,网上查了下资料学习,这里记录一下。

    collect方法,它是一个能够把stream管道中的结果集装进一个List集合的终极操作。 collect是一个把stream规约成一个value的规约操作,这里的value可以是一个Collection、Map或者一个value对象。在下面这几种情况下,可以使用collect操作。

  1. 把stream规约到一个单独的值 stream的执行结果可以规约成一个单独的值,这个单独的值可以是Collection或者数值型的值如int、double等,还可以是一个自定义的值对象。

  2. 在stream中对元素进行分组 对stream中的所有task按照TaskType分组。这会生成一个一个Map<TaskType,List<Task>,其中的每个entry都包含一个TaskType和与它相关联的Task。也可以使用其它任何的Collection来替代List。如果不需要把所有的task对应到一个TaskType,也可以生成一个Map<TaskType,Task>

  3. 分离stream中的元素 可以把一个stream分离到两个组中--正在进行中的和已经完成的task。

1 Collector in Action

下面我们通过这个根据type来对task进行分组的例子,来体验Collector的作用。在java8中,我们可以像下面这样来实现根据TaskType分组。请参考博客day 2

private static Map<TaskType, List<Task>> groupTasksByType(List<Task> tasks) {
    return tasks.stream().collect(Collectors.groupingBy(task -> task.getType()));
}

上面的代码使用了Collectors工具类中定义的groupingBy Collector方法。它创建一个map,其中key为TaskType、value为所有具有相同TaskType的task组成的一个list列表。在java7中要实现相同的功能,需要写如下的代码。

public static void main(String[] args) {
    List<Task> tasks = getTasks();
    Map<TaskType, List<Task>> allTasksByType = new HashMap<>();
    for (Task task : tasks) {
        List<Task> existingTasksByType = allTasksByType.get(task.getType());
        if (existingTasksByType == null) {
            List<Task> tasksByType = new ArrayList<>();
            tasksByType.add(task);
            allTasksByType.put(task.getType(), tasksByType);
        } else {
            existingTasksByType.add(task);
        }
    }
    for (Map.Entry<TaskType, List<Task>> entry : allTasksByType.entrySet()) {
        System.out.println(String.format("%s =>> %s", entry.getKey(), entry.getValue()));
    }
}

2 收集器(Collectors):常用规约操作

      Collectors 工具类提供了许多静态工具方法来为大多数常用的用户用例创建收集器,比如将元素装进一个集合中、将元素分组、根据不同标准对元素进行汇总等。本文中将覆盖大多数常见的收集器(Collector)

2.1 规约到一个单独的值

      如上面所说,收集器(collector)可以用来把stream收集到一个collection中或者产生一个单独的值。

2.1.1 把数据装进一个list列表中

下面我们给出第一个测试用例--将给定的任务列表的所有标题收集到一个List列表中。

import static java.util.stream.Collectors.toList;

public class Example2_ReduceValue {
    public List<String> allTitles(List<Task> tasks) {
        return tasks.stream().map(Task::getTitle).collect(toList());
    }
}

toList收集器通过使用List的add方法将元素添加到一个结果List列表中,toList收集器使用ArrayList作为List的实现。

2.1.2 将数据收集到一个Set中

      如果要保证所收集的title不重复并且我们对数据的排序没有要求的话,可以采用toSet收集器。

import static java.util.stream.Collectors.toSet;

public Set<String> uniqueTitles(List<Task> tasks) {
    return tasks.stream().map(Task::getTitle).collect(toSet());
}

  toSet 方法采用HashSet作为Set的实现来储存结果集。

2.1.3 把数据收集到一个Map中

      可以使用toMap收集器将一个stream转换成一个Map。toMap收集器需要两个集合函数来提取map中的key和value。下面的代码中,Task::getTitle需要一个task并产生一个仅有一个标题的key。task -> task是一个用来返回自己的lambda表达式,上例中返回一个task。

private static Map<String, Task> taskMap(List<Task> tasks) {
  return tasks.stream().collect(toMap(Task::getTitle, task -> task));
}

      可以使用Function接口中的默认方法identity来让上面的代码代码变得更简洁明了、传递开发者意图时更加直接,下面是采用identity函数的代码。

import static java.util.function.Function.identity;

private static Map<String, Task> taskMap(List<Task> tasks) {
  return tasks.stream().collect(toMap(Task::getTitle, identity()));
}

代码创建了一个Map,当出现相同的key时就会抛出如下的异常

Exception in thread "main" java.lang.IllegalStateException: 
Duplicate key Task{title='Read Version Control with Git book', type=READING}
at java.util.stream.Collectors.lambda$throwingMerger$105(Collectors.java:133)

  toMap还有一个可以指定合并函数的变体,我们可以采用它来处理重复的副本。合并函数允许开发者指定一个解决同一个key冲突的规则。在下面的代码中,我们简单地使用最后一个value,当然你也可以写更加智能的算法来处理冲突。

private static Map<String, Task> taskMap_duplicates(List<Task> tasks) {
  return tasks.stream().collect(toMap(Task::getTitle, identity(), (t1, t2) -> t2));
}

我们还可以使用toMap的第三种变体方法来使用任何其它的Map实现,这需要指定MapSupplier来存放结果。

public Map<String, Task> collectToMap(List<Task> tasks) {
    return tasks.stream().collect(toMap(Task::getTitle, identity(), (t1, t2) -> t2, LinkedHashMap::new));
}

toMap收集器类似,toConcurrentMap收集器可以产生ConcurrntMap来替代HashMap

3 Using other collections 使用其它的集合

toListtoSet等特定的收集器不支持指定潜在的list或set的实现,当你想要像下面这样这样把结果聚合到其它类型的集合时可以采用toCollection收集器。

private static LinkedHashSet<Task> collectToLinkedHaskSet(List<Task> tasks) {
  return tasks.stream().collect(toCollection(LinkedHashSet::new));
}

找出标题最长的task

public Task taskWithLongestTitle(List<Task> tasks) {
    return tasks.stream().collect(collectingAndThen(maxBy((t1, t2) -> t1.getTitle().length() - t2.getTitle().length()), Optional::get));
}

统计tags的总数

public int totalTagCount(List<Task> tasks) {
    return tasks.stream().collect(summingInt(task -> task.getTags().size()));
}

生成task标题的汇总

import static java.util.stream.Collectors.joining;

public String titleSummary(List<Task> tasks) {
    return tasks.stream().map(Task::getTitle).collect(joining(";"));
}

将元素分组

Collector收集器一个最常见的用户用例就是对元素进行分组,下面我们通过几个示例来理解我们可以如何来分组。

Example 1: 根据type对tasks分组

下面这个例子,我们根据TaskType对task进行分组。通过使用Collectors工具类的groupingBy收集器,我们可以非常简单的完成这个功能。可以使用方法引用和静态引入来让代码变得更加简洁。

import static java.util.stream.Collectors.groupingBy;
private static Map<TaskType, List<Task>> groupTasksByType(List<Task> tasks) {
       return tasks.stream().collect(groupingBy(Task::getType));
}

会产生如下的输出:

{
CODING=[
Task{title='Write a mobile application to store my tasks', type=CODING, createdOn=2015-07-03}], 
WRITING=[Task{title='Write a blog on Java 8 Streams', type=WRITING, createdOn=2015-07-04}], 
READING=[Task{title='Read Version Control with Git book', type=READING, createdOn=2015-07-01}, 
Task{title='Read Java 8 Lambdas book', type=READING, createdOn=2015-07-02}, 
Task{title='Read Domain Driven Design book', type=READING, createdOn=2015-07-05}
]
}

Example 2: 根据tags分组

private static Map<String, List<Task>> groupingByTag(List<Task> tasks) {
        return tasks.stream().
                flatMap(task -> task.getTags().stream().map(tag -> new TaskTag(tag, task))).
                collect(groupingBy(TaskTag::getTag, mapping(TaskTag::getTask,toList())));
}

    private static class TaskTag {
        final String tag;
        final Task task;

        public TaskTag(String tag, Task task) {
            this.tag = tag;
            this.task = task;
        }

        public String getTag() {
            return tag;
        }

        public Task getTask() {
            return task;
        }
    }

Example 3: 根据tag和tag的个数分组

private static Map<String, Long> tagsAndCount(List<Task> tasks) {
        return tasks.stream().
        flatMap(task -> task.getTags().stream().map(tag -> new TaskTag(tag, task))).
        collect(groupingBy(TaskTag::getTag, counting()));
    }

Example 4: 根据TaskType和createdOn分组

private static Map<TaskType, Map<LocalDate, List<Task>>> groupTasksByTypeAndCreationDate(List<Task> tasks) {
        return tasks.stream().collect(groupingBy(Task::getType, groupingBy(Task::getCreatedOn)));
    }

分割

有时候,你需要根据一定的规则将一个数据集分成两个数据集。比如,我们可以定义一个分割函数,根据规则进行时间早于今天和进行时间晚于今天将task分成两组。

private static Map<Boolean, List<Task>> partitionOldAndFutureTasks(List<Task> tasks) {
  return tasks.stream().collect(partitioningBy(task -> task.getDueOn().isAfter(LocalDate.now())));
}

生成统计

另外,一些产生统计结果的收集器也非常有用。它们主要用于int、double、long等基本类型上,它们可以用来产生类似如下的统计结果。

IntSummaryStatistics summaryStatistics = tasks.stream().map(Task::getTitle).collect(summarizingInt(String::length));
System.out.println(summaryStatistics.getAverage()); //32.4
System.out.println(summaryStatistics.getCount()); //5
System.out.println(summaryStatistics.getMax()); //44
System.out.println(summaryStatistics.getMin()); //24
System.out.println(summaryStatistics.getSum()); //162

还有一些其它的基本类型的变体,比如LongSummaryStatisticsDoubleSummaryStatistics

还可以用combine操作来把两个IntSummaryStatistics结合到一起。

firstSummaryStatistics.combine(secondSummaryStatistics);
System.out.println(firstSummaryStatistics)

把所有的titles连在一起

import static java.util.stream.Collectors.joining;

private static String allTitles(List<Task> tasks) {
  return tasks.stream().map(Task::getTitle).collect(joining(", "));
}

编写一个自定义的收集器

import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;

import java.util.Collections;
import java.util.EnumSet;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;

public class MultisetCollector<T> implements Collector<T, Multiset<T>, Multiset<T>> {

    @Override
    public Supplier<Multiset<T>> supplier() {
        return HashMultiset::create;
    }

    @Override
    public BiConsumer<Multiset<T>, T> accumulator() {
        return (set, e) -> set.add(e, 1);
    }

    @Override
    public BinaryOperator<Multiset<T>> combiner() {
        return (set1, set2) -> {
            set1.addAll(set2);
            return set1;
        };
    }

    @Override
    public Function<Multiset<T>, Multiset<T>> finisher() {
        return Function.identity();
    }

    @Override
    public Set<Characteristics> characteristics() {
        return Collections.unmodifiableSet(EnumSet.of(Characteristics.IDENTITY_FINISH));
    }
}
import com.google.common.collect.Multiset;

import java.util.Arrays;
import java.util.List;

public class MultisetCollectorExample {

    public static void main(String[] args) {
        List<String> names = Arrays.asList("shekhar", "rahul", "shekhar");
        Multiset<String> set = names.stream().collect(new MultisetCollector<>());

        set.forEach(str -> System.out.println(str + ":" + set.count(str)));

    }
}

Word Count in Java 8 Java8中的单词统计

下面,我们通过java8中的Streams和Collectors编写一个非常著名的单词统计示例来结束本文。

public static void wordCount(Path path) throws IOException {
    Map<String, Long> wordCount = Files.lines(path)
            .parallel()
            .flatMap(line -> Arrays.stream(line.trim().split("\\s")))
            .map(word -> word.replaceAll("[^a-zA-Z]", "").toLowerCase().trim())
            .filter(word -> word.length() > 0)
            .map(word -> new SimpleEntry<>(word, 1))
            .collect(groupingBy(SimpleEntry::getKey, counting()));
    wordCount.forEach((k, v) -> System.out.println(String.format("%s ==>> %d", k, v)));
}

例子

Hosting.java

package com.mkyong.java8

public class Hosting {

    private int Id;
    private String name;
    private long websites;

    public Hosting(int id, String name, long websites) {
        Id = id;
        this.name = name;
        this.websites = websites;
    }

    //getters, setters and toString()

}

1. List 转换为 Map – Collectors.toMap()

创建 Hosting 的list,使用Collectors.toMap 转换为 Map.

TestListMap.java

package com.mkyong.java8

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class TestListMap {

    public static void main(String[] args) {

        List<Hosting> list = new ArrayList<>();
        list.add(new Hosting(1, "liquidweb.com", 80000));
        list.add(new Hosting(2, "linode.com", 90000));
        list.add(new Hosting(3, "digitalocean.com", 120000));
        list.add(new Hosting(4, "aws.amazon.com", 200000));
        list.add(new Hosting(5, "mkyong.com", 1));

        // key = id, value - websites
        Map<Integer, String> result1 = list.stream().collect(Collectors.toMap(Hosting::getId, Hosting::getName));

        System.out.println("Result 1 : " + result1);

        // key = name, value - websites
        Map<String, Long> result2 = list.stream().collect(Collectors.toMap(Hosting::getName, Hosting::getWebsites));

        System.out.println("Result 2 : " + result2);

        // Same with result1, just different syntax
        // key = id, value = name
        Map<Integer, String> result3 = list.stream().collect(Collectors.toMap(x -> x.getId(), x -> x.getName()));

        System.out.println("Result 3 : " + result3);
    }
}

输出:

Result 1 : {1=liquidweb.com, 2=linode.com, 3=digitalocean.com, 4=aws.amazon.com, 5=mkyong.com}
Result 2 : {liquidweb.com=80000, mkyong.com=1, digitalocean.com=120000, aws.amazon.com=200000, linode.com=90000}
Result 3 : {1=liquidweb.com, 2=linode.com, 3=digitalocean.com, 4=aws.amazon.com, 5=mkyong.com}

附加类型转换:int-->string

numbers.stream().map(number -> String.valueOf(number)).collect(Collectors.joining(", "))

public static void main(String[] argv) {
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7);

    System.out.println(numbers.stream().map(number -> String.valueOf(number)).collect(Collectors.joining(", ")));
  }
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);

StringBuilder b = new StringBuilder();
list.forEach(b::append);

System.out.println(b);

//或者

String s = list.stream().map(Object::toString).collect(Collectors.joining(","));

2. List to Map – Duplicated Key!

2.1 运行下面代码,重复key会报错!

TestDuplicatedKey.java

package com.mkyong.java8;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class TestDuplicatedKey {

    public static void main(String[] args) {

        List<Hosting> list = new ArrayList<>();
        list.add(new Hosting(1, "liquidweb.com", 80000));
        list.add(new Hosting(2, "linode.com", 90000));
        list.add(new Hosting(3, "digitalocean.com", 120000));
        list.add(new Hosting(4, "aws.amazon.com", 200000));
        list.add(new Hosting(5, "mkyong.com", 1));
        
        list.add(new Hosting(6, "linode.com", 100000)); // new line

        // key = name, value - websites , but the key 'linode' is duplicated!?
        Map<String, Long> result1 = list.stream().collect(Collectors.toMap(Hosting::getName, Hosting::getWebsites));

        System.out.println("Result 1 : " + result1);

    }
}

输出–  报错:

Exception in thread "main" java.lang.IllegalStateException: Duplicate key 90000
	at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)
	at java.util.HashMap.merge(HashMap.java:1245)
	//...

2.2 解决重复key问题,传递处理函数参数:

    Map<String, Long> result1 = list.stream().collect(
                Collectors.toMap(Hosting::getName, Hosting::getWebsites, (oldValue, newValue) -> oldValue)
        );

输出:

Result 1 : {..., aws.amazon.com=200000, linode.com=90000}

注意:
(oldValue, newValue) -> oldValue ==> 如果key重复,使用oldKey

3.3 尝试使用newValue

    Map<String, Long> result1 = list.stream().collect(
                Collectors.toMap(Hosting::getName, Hosting::getWebsites,(oldValue, newValue) -> newvalue)
        );

输出:

Result 1 : {..., aws.amazon.com=200000, linode.com=100000}

3. List to Map – Sort & Collect

TestSortCollect.java

package com.mkyong.java8;

import java.util.*;
import java.util.stream.Collectors;

public class TestSortCollect {

    public static void main(String[] args) {

        List<Hosting> list = new ArrayList<>();
        list.add(new Hosting(1, "liquidweb.com", 80000));
        list.add(new Hosting(2, "linode.com", 90000));
        list.add(new Hosting(3, "digitalocean.com", 120000));
        list.add(new Hosting(4, "aws.amazon.com", 200000));
        list.add(new Hosting(5, "mkyong.com", 1));
        list.add(new Hosting(6, "linode.com", 100000));

        //example 1
        Map result1 = list.stream()
                .sorted(Comparator.comparingLong(Hosting::getWebsites).reversed())
                .collect(
                        Collectors.toMap(
                                Hosting::getName, Hosting::getWebsites, // key = name, value = websites
                                (oldValue, newValue) -> oldValue,       // if same key, take the old key
                                LinkedHashMap::new                       // returns a LinkedHashMap, keep order
                        ));

        System.out.println("Result 1 : " + result1);

    }
}

Result 1 : {aws.amazon.com=200000, digitalocean.com=120000, linode.com=100000, liquidweb.com=80000, mkyong.com=1}

P.S

上面例子中, stream在collect前已经被排序了, 所以“linode.com=100000” 已经成为 ‘oldValue’.

枚举例子使用

public enum MyCodeEnum {
    ERR_1                ("1", "1 error"),
    ERR_2                ("2", "2 error");
    
    private String code;
    private String message;

    MyCodeEnum(String c, String m) {
        code = c;
        message = m;
    }

    public String getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

    @Override
    public String toString(){
        return "{code:\""+this.code + "\",message:\"" + this.message + "\"}";
    }

    public Map<String, String> toHashMap(){
        return new HashMap<String, String>(){{
            put("code", code);
            put("message", message);
        }};
    }

    /* Reverse Lookup Implementation */
    private static final Map<String, Map<String, String>> lookup
            = Arrays.asList(values()).stream().collect(toMap(MyCodeEnum::getCode, MyCodeEnum::toHashMap));

    public static Map<String, String> getMapValue(String aKey) {
        return lookup.get(aKey);
    }
    
    
    private static final Map<String, String> lookup2
            = Arrays.asList(values()).stream().collect(toMap(MyCodeEnum::getCode, MyCodeEnum::getMessage));
    public static boolean containsKey(String aKey) {
        return lookup2.containsKey(aKey);
    }
    public static String getKeyValue(String aKey) {
        return lookup2.get(aKey);
    }
}

上面例子中:

    Arrays.asList(values())是枚举初始化时将所有枚举都组合成一个list。

 Java program to convert `List<Integer>` to `List<String>`

List<String> newList = list.stream()
                                    .map(s -> String.valueOf(s))
                                    .collect(Collectors.toList());

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
 
// Java program to convert `List<Integer>` to `List<String>`
class Main
{
    // Program to convert `List<Integer>` to `List<String>`
    public static void main(String[] args)
    {
        List<Integer> list = Arrays.asList( 1, 2, 3, 4, 5 );
 
        List<String> newList = list.stream()
                                    .map(s -> String.valueOf(s))
                                    .collect(Collectors.toList());
        System.out.println(newList);
    }
}

import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
 
// Java program to convert `List<Integer>` to `List<String>`
class Main
{
    // Generic method to convert `List<Integer>` to `List<String>`
    public static <T, U> List<U> transform(List<T> list,
                                        Function<T, U> function)
    {
        return list.stream()
                    .map(function)
                    .collect(Collectors.toList());
    }
 
    // Program to convert `List<Integer>` to `List<String>`
    public static void main(String[] args)
    {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
 
        List<String> newList = transform(list, String::valueOf);
        System.out.println(newList);
    }
}

#############################

Java stream流 修改List<String> 和 List<对象>

list.stream().filter(dto-> 筛选条件).forEach(dto2-> dto2.setId("1"));

/**
* List<String> 无法for循环修改  用jdk8新特性  stream流
* */
List<String> list = Arrays.asList("1" , "1" ,"1" ,"1" ,"1" ,"1");
 
// 给 list 元素都添加 2 标识
System.out.println(list.stream().map(x -> x + 2).collect(Collectors.toList()));

输出:[12, 12, 12, 12, 12, 12]

修改对象:

List<DataX> dataXES = JSON.parseArray(payload, DataX.class);
//dataXES = dataXES.stream().peek(x -> x.setTss("11111"))).collect(Collectors.toList());

dataXES = dataXES.stream().peek(x -> {x.setTss("11111");  x.setTss2("22222");})).collect(Collectors.toList());


System.out.println(dataXES);
 

###########################################

java8 list.forEach() 修改值失败

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {

    private String name;

    public static void main(String[] args) {
        List<User> list = new ArrayList<>();
        list.add(new User("bb"));
        list.forEach(u -> {
            u.setName("aaa");
        });
        System.out.println(list);

        List<String> list2 = new ArrayList<>();
        list2.add("bb");
        list2.forEach(u -> {
            u = "aaa";
        });
        System.out.println(list2);

        List<String[]> list3 = new ArrayList<>();
        list3.forEach(arr -> {
            arr = new String[]{"aaa"};
        });
        System.out.println(list3);
    }
}

结果

[User(name=aaa)]
[bb]
[]

原因
list.forEach掉的底层是ArrayList的forEach

@Override
    public void forEach(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        final int expectedModCount = modCount;
        @SuppressWarnings("unchecked")
        final E[] elementData = (E[]) this.elementData;
        final int size = this.size;
        for (int i=0; modCount == expectedModCount && i < size; i++) {
            action.accept(elementData[i]);
        }
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }



final E[] elementData = (E[]) this.elementData; 做了传参,然后把赋予的参数带到了action.accept(elementData[i]);中执行

如果是String、String[] 等基础类型,就会是值传递,导致后续的赋值失败
如果是引用对象类型,则传递得是引用,赋值会成功

 

#############################################

Map 转另一个Map

//示例1 Map<String, List<String>> 转 Map<String,User>
Map<String,List<String>> map = new HashMap<>();
map.put("java", Arrays.asList("1.7", "1.8"));
map.entrySet().stream();

@Getter
@Setter
@AllArgsConstructor
public static class User{
    private List<String> versions;
}

Map<String, User> collect = map.entrySet().stream()
                .collect(Collectors.toMap(
                        item -> item.getKey(),
                        item -> new User(item.getValue())));

//示例2 Map<String,Integer>  转 Map<String,Double>
Map<String, Integer> pointsByName = new HashMap<>();
Map<String, Integer> maxPointsByName = new HashMap<>();

Map<String, Double> gradesByName = pointsByName.entrySet().stream()
        .map(entry -> new AbstractMap.SimpleImmutableEntry<>(
                entry.getKey(), ((double) entry.getValue() /
                        maxPointsByName.get(entry.getKey())) * 100d))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

stream流 转 Map中

假设有一个User实体类,有方法getId(),getName(),getAge()等方法,示例如下:

Stream<User> userStream = Stream.of(new User(0, "张三", 18), new User(1, "张四", 19), new User(2, "张五", 19), new User(3, "老张", 50));

Map<Integer, User> userMap = userSteam.collect(Collectors.toMap(User::getId, item -> item));

字典查询和数据转换 toMap时,如果value为null,会报空指针异常

解决办法一:

Map<String, List<Dict>> resultMaps = Arrays.stream(dictTypes) .collect(Collectors.toMap(i -> i, i -> Optional.ofNullable(dictMap.get(i)).orElse(new ArrayList<>()), (k1, k2) -> k2));

解决办法二:

Map<String, List<Dict>> resultMaps = Arrays.stream(dictTypes) .filter(i -> dictMap.get(i) != null).collect(Collectors.toMap(i -> i, dictMap::get, (k1, k2) -> k2));

解决办法三:

Map<String, String> memberMap = list.stream().collect(HashMap::new, (m,v)-> m.put(v.getId(), v.getImgPath()),HashMap::putAll); System.out.println(memberMap);

解决办法四:

Map<String, String> memberMap = new HashMap<>(); list.forEach((answer) -> memberMap.put(answer.getId(), answer.getImgPath())); System.out.println(memberMap); Map<String, String> memberMap = new HashMap<>(); for (Member member : list) { memberMap.put(member.getId(), member.getImgPath()); }

#############################################

新特性Stream

Stream语法讲解

Stream执行流程很简单,主要有三个,首先创建一个Stream,然后使用Stream操作数据,最后终止Stream。有点类似于Stream的生命周期。下面我们根据其流程来一个一个讲解。

1、前提准备

首先我们创建一个Student类,以后我们每次都是操作这个类

java8中一个极其强悍的新特性Stream(非常实用)

然后下面我们再创建一个StudentData类,用于获取其数据

java8中一个极其强悍的新特性Stream(非常实用)

我们只需要把方法变成static类型的就可以了。

2、创建一个Stream

方式一:通过一个集合创建Stream

java8中一个极其强悍的新特性Stream(非常实用)

方式二:通过一个数组创建Stream

java8中一个极其强悍的新特性Stream(非常实用)

方式三:通过Stream.of

java8中一个极其强悍的新特性Stream(非常实用)

方式四:创建一个无限流

java8中一个极其强悍的新特性Stream(非常实用)

3、使用Stream操作数据

操作1:筛选和切片

java8中一个极其强悍的新特性Stream(非常实用)

操作2:映射

java8中一个极其强悍的新特性Stream(非常实用)

操作3:排序

java8中一个极其强悍的新特性Stream(非常实用)

4、终止Stream

操作1:匹配和查找

java8中一个极其强悍的新特性Stream(非常实用)

操作2:归约

java8中一个极其强悍的新特性Stream(非常实用)

操作3:收集

java8中一个极其强悍的新特性Stream(非常实用)

stream基本的语法就是这样,你会发现Stream就像是一个工具一样,可以帮我们分析处理数据,极其的好用,但是目前还不知道其效率如何。根据网上一位大佬的内存时间分析,其实在数据量比较庞大的时候,Stream可以为我们节省大量的时间,数据量小的时候并不明显

参考

  1. Java 8 Collectors JavaDoc
  2. Java 8 – How to sort a Map
  3. Java 8 Lambda : Comparator example
  4. 【译】java8之collector - 简书
  5. Java 8 - Convert List to Map - Mkyong.com
  6. https://www.cnblogs.com/hiver/p/9156147.html

请我喝咖啡

如果觉得文章写得不错,能对你有帮助,可以扫描我的微信二维码请我喝咖啡~~哈哈~~

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java 8的groupingBy是一个用于对集合进行分组的方法。它是Stream API中的一个终端操作,可以基于给定的分类函数将元素分组成一个Map。 下面是groupingBy方法的基本语法: ``` public static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(Function<? super T, ? extends K> classifier) ``` 其中,T表示集合中的元素的类型,K表示分组的键的类型。classifier是一个函数,用于根据元素生成分组的键。 下面是一个示例,演示如何使用groupingBy对一个字符串集合进行分组,按照字符串的长度进行分组: ```java List<String> strings = Arrays.asList("apple", "banana", "cat", "dog", "elephant", "fish"); Map<Integer, List<String>> result = strings.stream() .collect(Collectors.groupingBy(String::length)); System.out.println(result); ``` 运行结果为: ``` {3=[cat, dog], 4=[fish], 5=[apple], 6=[banana], 8=[elephant]} ``` 在这个例子中,我们使用String::length作为分类函数,将字符串按照长度分组成一个Map,其中键是字符串的长度,值是具有相同长度的字符串列表。 除了上述示例,groupingBy还可以与其他收集器一起使用,实现更复杂的分组操作。例如,我们可以使用groupingBy结合counting收集器来统计每个分组中元素的数量: ```java Map<Integer, Long> result = strings.stream() .collect(Collectors.groupingBy(String::length, Collectors.counting())); System.out.println(result); ``` 运行结果为: ``` {3=2, 4=1, 5=1, 6=1, 8=1} ``` 这个例子中,我们使用groupingBy和counting收集器结合,统计每个分组中元素的数量。 总结:Java 8的groupingBy方法是一个强大的工具,可以帮助我们对集合进行灵活的分组操作。它通过分类函数将元素分组成一个Map,可以与其他收集器一起使用,实现更复杂的分组统计等操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值