大数据------javase基础------day15

Map

  • Map接口的特点

Map接口是键值对集合,每个元素均包含键和值两个对象

无序(存入顺序和遍历顺序不一致)

  • 键值对特点:

(1)键唯一,不可重复;但值可以重复

(2)键和值一一映射,一个键对应一个值(值可以是单个值也可以是个数组或集合)

  • 创建Map接口方式

(1)以多态的方式创建

(2)具体的实现类HashMap

Map接口常用方法

方法解释
public V put(K key, V value)将键值对存入集合
public V get(Object key)返回指定键映射到的值,如果此映射不包含键的映射,则返回 null 。 (即用键取值)
pulblic int size()返回此映射中键 - 值映射的数量。 (即返回该集合中键值对元素数量)
public V remove(Object key)如果存在,则从该映射中移除键的映射(可选操作)。(即使用键作为检索条件来删除键值对)
default boolean remove(Object key, Object value)仅当指定键当前映射到指定值时才删除该条目的条目。
public boolean isEmpty()如果此映射不包含键 - 值映射,则返回 true 。(即判断该集合是否为空)
public boolean containsKey(Object key)如果此映射包含指定键的映射,则返回 true 。 (即判断该集合中是否存在指定的键)
public boolean containsValue(Object value)如果此映射将一个或多个键映射到指定值,则返回 true 。(即判断该集合中是否有指定的值)
default V replace(K key, V value)仅当指定键当前映射到某个值时,才替换该条目的条目。 (即用新值替换当前键所映射的值)
default boolean replace(K key, V oldValue, V newValue)仅当前映射到指定值时,才替换指定键的条目。

注意:使用put方法时,若要存入的键是集合中已经存在的键的话,则此时会用新值替换掉原来该键所对应的值

public class TestOne {
    public static void main(String[] args) {
        Map map = new HashMap<>();
        map.put("班长", "美国");
        map.put("学委", "中国");//存入键值对
        map.put("体委", "俄罗斯");
        map.put("文委", "意大利");
        map.put("班委", "意大利");
        String s = (String) map.get("班长");//取出键所对应的值
        System.out.println(s);
        System.out.println(map);
        System.out.println("---------------------------");
        map.remove("学委");//使用键作为检索条件来删除键值对
        System.out.println(map);
        System.out.println("---------------------------");
        map.remove("班委", "意大利");
        System.out.println(map);
        System.out.println("---------------------------");
        System.out.println(map.isEmpty());
        System.out.println("---------------------------");
        System.out.println(map.containsKey("班委"));//判断集合中是否有指定的键
        System.out.println("---------------------------");
        System.out.println(map.containsValue("意大利"));//判断该集合中是否有指定的值
        System.out.println("---------------------------");
        map.replace("班长", "朝鲜");
        System.out.println(map);
        System.out.println("---------------------------");
        // 仅当前映射到指定值时,才替换指定键的条目
        System.out.println(map.replace("文委", "德国", "日本"));
        System.out.println("---------------------------");
        map.put("文委", "韩国");
        System.out.println(map);

    }
}

请添加图片描述

Map接口的遍历

  • 注意

(1)Map集合不能用iterator迭代器遍历,因为iterator()方法属于Collection集合中的方法

(2)Map集合也不能用增强for循环遍历,因为其底层用的是iterator迭代器遍历

(3)Map集合也不能用传统for循环遍历,因为其没有下标

(4)Map接口的遍历需要遍历键,因为每个键对应一个值

  • 用到的方法
方法解释
public Set<K> keySet()返回此映射中包含的键的Set视图。 (即返回键集,该键集会被存入不可重复的Set集合,从而可以使用iterator迭代器遍历)
public Collection<V> values()返回此映射中包含的值的Collection视图。 (即返回值集,该值集会被存入可重复的Collection集合,从而可以使用iterator迭代器进行遍历)
public Set<Map.Entry<K,V>> entrySet()返回此映射中包含的映射的Set视图。 (即返回键值对集)
用到的静态嵌套类解释
static interface Map.Entry<K,V>映射条目(键值对)。 (即将获取到的键值转型为键值对)
静态嵌套类Entry中的方法解释
public K getKey()返回与此条目对应的键。
public V getValue()返回与此条目对应的值。

问题

(1)为什么键集存入Collection集合中的Set集合?

因为键是无序且不可以重复的,而Set集合刚好是无序且不可重复的,所以若想要对Map集合进行遍历则需要先将其键集转换为Set集合,然后利用Collection集合中特有的方法iterator方法进行迭代器遍历

(2)为什么值集存入Collection集合?

因为值是跟着键走的,因为键是无序的,所以值是无序的,又因为虽然键是不能重复的,但是值是可以重复的,所以无法使用Collection集合中的两个子集合(List和Set),只能使用Collection集合

  • 遍历方式一------利用键集遍历
public class TestTwo {
    public static void main(String[] args) {
        Map map = new HashMap<>();
        map.put("班长", "美国");
        map.put("学委", "中国");//存入键值对
        map.put("体委", "俄罗斯");
        map.put("文委", "意大利");
        map.put("班委", "意大利");

        Set keys = map.keySet();//返回键集---该键集会被存入Set集合,从而可以使用iterator迭代器遍历
        Iterator iter = keys.iterator();//获取键集迭代器
        while(iter.hasNext()) {
            String key = (String) iter.next();
            String value = (String) map.get(key);
            System.out.println(key + "=" + value);
        }
    }
}

在这里插入图片描述

  • 遍历方式二------利用值集遍历
public class TestThree {
    public static void main(String[] args) {
        Map map = new HashMap<>();
        map.put("班长", "美国");
        map.put("学委", "中国");//存入键值对
        map.put("体委", "俄罗斯");
        map.put("文委", "意大利");
        map.put("班委", "意大利");

        Collection col = map.values();
        Iterator iter = col.iterator();
        while (iter.hasNext()) {
            String value = (String) iter.next();
            System.out.println(value);
        }
    }
}

在这里插入图片描述

  • 遍历方式三------利用键值集遍历
public class TestFour {
    public static void main(String[] args) {
        Map map = new HashMap<>();
        map.put("班长", "美国");
        map.put("学委", "中国");//存入键值对
        map.put("体委", "俄罗斯");
        map.put("文委", "意大利");
        map.put("班委", "意大利");

        Set set = map.entrySet();//返回键值对Set集合
        Iterator iter = set.iterator();//返回迭代器对象
        while (iter.hasNext()) {
            Map.Entry entry = (Map.Entry) iter.next();//将获取到的元素转换为键值对
            System.out.println(entry);//打印出键值对
            String key = (String) entry.getKey();//返回键
            String value = (String) entry.getValue();//返回值
            System.out.println("key = " + key + "\n" + "value = " + value);
            System.out.println("------------------------------");

        }
    }
}

在这里插入图片描述

  • Map接口的实现类

Map接口的常用实现类:HashMap、TreeMap、LinkedHashMap和Properties

HashMap和HashTable

  • 异同

(1)HashMap线程不安全,允许使用null键和null值,效率高,推荐使用

(2)HashTable线程安全,任何非null对象都可以用作键或值,效率低,不推荐使用

(3)底层用相同的方式(哈希表)存储元素

  • HashMap的实例有两个影响其性能的参数:初始容量和负载因子

(1)初始容量只是创建哈希表时的容量

(2)是在自动增加容量之前允许哈希表获取的完整程度的度量。 当哈希表中的条目数超过默认加载因子(0.75)和当前容量的乘积时,哈希表将被重新哈希(即,重建内部数据结构),以便哈希表具有大约两倍的桶数。

  • HashMap特点
    (1)创建HashMap对象时会为负载因子进行默认赋值为0.75
    (2)第一次调用put方法时初始化一个长度为16的数组
    (3)当集合中的元素数超过默认负载因子(0.75)和数组长度的乘积时按照原来数组长度的两倍扩容
    (4)在JDK1.8之前,HashMap底层使用链表+数组存储元素;在JDK1.8之后,HashMap底层使用链表+数组+红黑树存储
    (5)在JDK1.8之后,若一个链表元素超过8个此时数组长度达到64,则将链表结构变为红黑树

  • HashMap源码解析
    (1)负载因子
    在这里插入图片描述
    (2)调用put方法
    在这里插入图片描述
    因为创建Map集合时是利用HashMap多态的方式创建的,所以调用的put方法为HashMap类中的put方法,以上源码截图即为HashMap类中的put方法,其中HashMap源码中的putVal方法代码及解释如下:

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
//tab为键值对数组,p为当前节点元素,n为当前数组长度,i为计算所得到的数组索引(即本次元素在数组中的下标)
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    //通过哈希码计算数组索引i并检查该位置的节点p是否为null,若为null则在该位置存入一个新节点
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    //通过哈希码计算数组索引i并检查该位置的节点p不为null(代表该节点处有元素)
    else {
        Node<K,V> e; K k;
         //判断当前节点处的元素的哈希值与要存入的元素的哈希值是否相等且当前节点处的元素的键是否等于要存入的元素的键以及两键是否相等,若成立则说明要存入的元素与原元素相同则不执行新增操作
        //该if判断等同于hashcode和equals方法的结合体
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;//将新元素赋值给老元素
        //若不成立则说明要存入的元素与原元素不相同,此时需要判断要新增的元素节点是否是TreeNode的对象(即判断当前要存的节点是否是树节点(即红黑树)的对象),若是则存入树中
        else if (p instanceof TreeNode)
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        //若新增的元素节点不是树节点的对象则代表当前节点是链表中的节点,将该节点插入链表中
        else {
            //遍历循环链表,直至链表末尾
            for (int binCount = 0; ; ++binCount) {
                //将链表的下一个节点赋给e并判断该节点是否为空,若为空则将节点p作为一个新节点添加到链表中
                if ((e = p.next) == null) {
                    p.next = newNode(hash, key, value, null);
                    //判断链表增加节点后长度是否达到树化的阈值,若达到则调用方法将链表转化为树
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;//退出循环执行循环体后的代码
                }
                //若存在与要插入的节点相同的节点则直接跳出循环
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;将当前链表节点赋给p
            }
        }
        if (e != null) { // existing mapping for key
            V oldValue = e.value;
            if (!onlyIfAbsent || oldValue == null)
                e.value = value;
            afterNodeAccess(e);
            return oldValue;
        }
    }
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
    }

if ((tab = table) == null || (n = tab.length) == 0)该句代码中table的代码为transient Node<K, V>[] table

if ((tab = table) == null || (n = tab.length) == 0)
    n = (tab = resize()).length;

table 的初始化是在 putVal 方法中进行的,当第一次添加元素时,table 会被初始化为具有默认容量的数组。所以第一次使用put方法时集合中无元素,最开始table为空,此句代码含义为:将装数据的数组赋值给键值对数组tab,然后判断tab是否为null或者当前数组tab的长度是否为0,若是(就证明无法添加新元素)则需要利用HashMap源码中的resize方法来返回一个扩容后的新数组并将其赋给tab然后将该扩容后的新数组的长度赋值给n

if ((p = tab[i = (n - 1) & hash]) == null)
    tab[i] = newNode(hash, key, value, null);

通过哈希码计算数组索引i并检查该位置的节点p是否为null,若为null则在该位置创建一个新节点

注意:为什么用(n-1)&hash ?

保证计算出的索引值在数组长度范围内

用到的resize方法的代码如下:

final Node<K,V>[] resize() {
    //将当前元素数组赋给oldTab------旧节点数组
        Node<K,V>[] oldTab = table;
    //记录当前oldTab旧节点数组的长度;若为空则代表第一次存储元素,将长度记为0,否则记为当前数组长度
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
    //记录长度*负载因子之后得出的旧的扩容临界值(即旧的阈值),用于判断是否需要进行扩容。其中临界值threshold在HashMap类的构造器中利用其方法tableSizeFor计算
        int oldThr = threshold;
    //newCap为新的数组长度,newThr为新的扩容临界值---即定义新的容量和新的阈值
        int newCap, newThr = 0;
    //判断是否是第一次存储元素,若不是则执行if代码
        if (oldCap > 0) {
            //判断旧节点数组长度是否大于常量(static final int MAXIMUM_CAPACITY= 1 << 30),该常量为HashMap的最大容量
            if (oldCap >= MAXIMUM_CAPACITY) {
                //若旧节点数组长度大于HashMap的最大容量则将阈值设为最大值常数2^31 -1,表示不再进行扩容,直接返回旧节点数组
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            //将旧节点数组长度扩大两倍赋给新节点newCap并判断若新容量小于MAXIMUM_CAPACITY(HashMap的最大容量)并且旧容量大于等于DEFAULT_INITIAL_CAPACITY(为HashMap的默认初始容量,为16),则将新阈值设为旧阈值的两倍。
            else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                     oldCap >= DEFAULT_INITIAL_CAPACITY)
                newThr = oldThr << 1; // double threshold
        }
    //若是第一次存储元素则判断旧阈值是否大于0
        else if (oldThr > 0) // initial capacity was placed in threshold
            newCap = oldThr;
    //若是第一次存储元素且旧阈值为0------即旧节点容量和旧阈值均为0  时,则将新容量设置为默认初始容量16,将新阈值设置为默认负载因子与默认初始容量的乘积12
        else {               // zero initial threshold signifies using defaults
            newCap = DEFAULT_INITIAL_CAPACITY;
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
    //若新阈值为0则判断新容量小于最大容量并且新容量乘以负载因子小于最大容量并为新阈值赋值
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr;//更新阈值(扩容的临界值)
        @SuppressWarnings({"rawtypes","unchecked"})
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;
        if (oldTab != null) {
            for (int j = 0; j < oldCap; ++j) {
                Node<K,V> e;
                if ((e = oldTab[j]) != null) {
                    oldTab[j] = null;
                    if (e.next == null)
                        newTab[e.hash & (newCap - 1)] = e;
                    else if (e instanceof TreeNode)
                        ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                    else { // preserve order
                        Node<K,V> loHead = null, loTail = null;
                        Node<K,V> hiHead = null, hiTail = null;
                        Node<K,V> next;
                        do {
                            next = e.next;
                            if ((e.hash & oldCap) == 0) {
                                if (loTail == null)
                                    loHead = e;
                                else
                                    loTail.next = e;
                                loTail = e;
                            }
                            else {
                                if (hiTail == null)
                                    hiHead = e;
                                else
                                    hiTail.next = e;
                                hiTail = e;
                            }
                        } while ((e = next) != null);
                        if (loTail != null) {
                            loTail.next = null;
                            newTab[j] = loHead;
                        }
                        if (hiTail != null) {
                            hiTail.next = null;
                            newTab[j + oldCap] = hiHead;
                        }
                    }
                }
            }
        }
        return newTab;
    }

Stream流

  • 例子引入:

例题:创建一个集合并存储多个字符串元素(“张三丰”, “张无忌”, “张翠山”, “王二麻子”, “张良”, “谢广坤”),然后把集合中所有以“张”开头的元素存储到一个新的集合,之后再把“张”开头的集合中的长度为3的元素存储到一个新的集合中,最后遍历所得到的集合

public class TestOne {
    public static void main(String[] args) {
        ArrayList<String> list1 = new ArrayList<>(List.of("张三丰", "张无忌", "张翠山", "王二麻子", "张良", "谢广坤"));
        ArrayList<String> list2 = new ArrayList<>();
        for (String s : list1) {
            if (s.startsWith("张")) list2.add(s);
        }
        ArrayList<String> list3 = new ArrayList<>();
        for (String s : list2) {
            if (s.length() == 3) list3.add(s);
        }
        for (String s : list3) {
            System.out.println(s);
        }
    }
}

在这里插入图片描述

以上代码是对集合分别进行各步骤的操作,有些繁琐,可以用Stream流来简化对集合的操作,如下代码所示:

public class TestOne {
    public static void main(String[] args) {
        ArrayList<String> list1 = new ArrayList<>(List.of("张三丰", "张无忌", "张翠山", "王二麻子", "张良", "谢广坤"));
        list1.stream().filter(s -> s.startsWith("张"))
                .filter(s -> s.length() == 3)
                .forEach(s -> System.out.println(s));
    }
}

在这里插入图片描述

Stream流三类方法

获取Stream流的方法

  • 作用

创建一条流水线,并把数据放到流水线上准备进行操作

  • 可获取Stream流的四种情况

(1) 单列集合

可以使用Collection接口中的默认方法stream()生成流default Stream<E> stream()

public class TestOne {
    public static void main(String[] args) {
        ArrayList<String> list1 = new ArrayList<>();
        list1.add("aaa");
        list1.add("bbb");
        list1.add("ccc");
        /*
        Stream<String> stream = list1.stream();
        stream.forEach(s -> System.out.println(s));
        */
        //Stream流的方法一般不会分开写,如下所示
        list1.stream().forEach(s -> System.out.println(s));
    }
}

在这里插入图片描述

(2)双列集合(不能直接获取Stream流)

间接的生成流,可以先通过keySet或者entrySet获取一个Set集合,然后在获取Stream流

获取键Stream流:必须先通过KeySet方法获取到所有的键,然后在把所有的键放到Stream流中

获取键值对Stream流:必须先通过entrySet方法获取到所有的键值对对象,然后再把Set集合中所有的键值对放到Stream流中

public class TestTwo {
    public static void main(String[] args) {
        HashMap<String, Integer> hm = new HashMap<>();
        hm.put("zhangsan", 23);
        hm.put("lisi", 24);
        hm.put("wangwu", 25);
        hm.put("zhaoliu", 26);
        hm.put("qianqi", 27);
    //双列集合不能直接获取Stream流,
        // 获取键Stream流:必须先通过KeySet获取到所有的键,然后在把所有的键放到Stream流中
        hm.keySet().stream().forEach(s -> System.out.println(s));
        System.out.println("------------------------");
        //获取键值对Stream流:必须先通过entrySet获取到所有的键值对对象,然后再把Set集合中所有的键值对放到Stream流中
        hm.entrySet().stream().forEach(i -> System.out.println(i));
    }
}

在这里插入图片描述

(3)数组

Arrays中的静态方法stream生成流

public static <T> Stream<T> stream(T[] array)------返回具有指定数组的序列stream流

public class TestTwo {
    public static void main(String[] args) {
        int[] arr = {1,2,3,4,5};
        Arrays.stream(arr).forEach(s -> System.out.println(s));
    }
}

在这里插入图片描述

(4)同种数据类型的多个数据(eg:1,2,3,…或"aaa",“bbb”,…)

前提:这些同种数据类型的多个数据并没有放到数组或者集合中时才可以获取Stream流

通过使用Stream类中的of方法生成流public static <T> Stream<T> of(T...values)

public class TestTwo {
    public static void main(String[] args) {
        Stream.of(1,2,3,4,5).forEach(s -> System.out.println(s));
    }
}

在这里插入图片描述

中间方法(Stream类中的方法)

  • 解释

流水线上的操作(一次操作完成之后还可继续进行其他操作)

  • 常用方法一
方法解释
public Stream<T> filter(Predicate<? super T> predicate)返回由与此给定谓词匹配的此流的元素所组成的流。即用于对流中的数据进行过滤
public void forEach(Consumer<? super T> action)对此流的每个元素执行操作。即迭代流中的每个元素,并对其执行指定的操作
public class TestTwo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("张三丰");
        list.add("张无忌");
        list.add("张翠山");
        list.add("王二麻子");
        list.add("张良");
        list.add("谢广坤");
        list.stream().filter(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.startsWith("张");
            }
        }).filter(new Predicate<String>() {
            @Override
            public boolean test(String s) {
                return s.length() == 3;
            }
        }).forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
    }
}

等同于

public class TestTwo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("张三丰");
        list.add("张无忌");
        list.add("张翠山");
        list.add("王二麻子");
        list.add("张良");
        list.add("谢广坤");
        //等同于
        list.stream().filter(s ->s.startsWith("张")).filter(s -> s.length() == 3).forEach(s -> System.out.println(s));
    }
}

在这里插入图片描述

  • 常用方法二
方法解释
public Stream<T> limit(long maxSize)返回由此流的元素组成的流,截短长度不能超过 maxSize 。 即截取原始流的前 maxSize 个元素作为新流
public Stream<T> skip(long n)在丢弃流的第一个 n元素后,返回由该流的剩余元素组成的流。即跳过流中的前 n 个元素,返回剩余的元素组成的新流
public class TestTwo {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("张三丰");
        list.add("张无忌");
        list.add("张翠山");
        list.add("王二麻子");
        list.add("张良");
        list.add("谢广坤");
        list.stream().filter(s ->s.startsWith("张")).forEach(s -> System.out.println(s));
        System.out.println("--------------以下为利用limit方法截取原始流筛选后的前2个作为新流-----------------");
        list.stream().filter(s ->s.startsWith("张")).limit(2).forEach(s -> System.out.println(s));
        System.out.println("--------------以下为利用skip方法跳过原始流筛选后的前2个元素,返回剩余的元素组成新流-----------------");
        list.stream().filter(s ->s.startsWith("张")).skip(2).forEach(s -> System.out.println(s));
    }
}

在这里插入图片描述

  • 常用方法三
方法解释
public static<T> Stream<T> concat(Stream a, Stream b)将a和b两个流合并为一个流
public Stream<T> distinct()去除流中的重复元素。底层依赖于hashCodeequals方法
public class TestTwo {
    public static void main(String[] args) {
        ArrayList<String> list1 = new ArrayList<>();
        list1.add("张三丰");
        list1.add("张无忌");
        list1.add("张翠山");
        ArrayList<String> list2 = new ArrayList<>();
        list2.add("王二麻子");
        list2.add("王二麻子");
        list2.add("张良");
        list2.add("张良");
        list2.add("谢广坤");
        list2.add("谢广坤");
        System.out.println("------------------以下为利用静态方法concat将两个流合并为一个流------------------");
        Stream.concat(list1.stream(), list2.stream()).forEach(s -> System.out.println(s));
        System.out.println("-----------------以下为利用distinct方法来去除流中的重复元素-------------------------");
        Stream.concat(list1.stream(), list2.stream()).distinct().forEach(s -> System.out.println(s));
    }
}

在这里插入图片描述

终结方法(Stream类中的方法)

  • 解释

一个Stream流只能有一个终结方法,是流水线上的最后一个操作

方法解释
public void forEach(Consumer<? super T> action)对此流的每个元素执行操作。
public long count()返回此流中的元素个数
public class TestTwo {
    public static void main(String[] args) {
        ArrayList<String> list1 = new ArrayList<>();
        list1.add("张三丰");
        list1.add("张无忌");
        list1.add("张翠山");
        list1.add("王二麻子");
        list1.add("张良");
        list1.add("谢广坤");
        System.out.println("流中的元素个数为:" + list1.stream().count() + "个");
        System.out.println("输出流中的元素:");
        list1.stream().forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
        //等同于list1.stream().forEach(s -> System.out.println(s));
    }
}

在这里插入图片描述

Stream流的收集操作

在Stream流中无法直接修改集合、数组等数据源中的数据

  • 例题示例

定义一个集合并添加一些整数1,2,3,4,5,6,7,8,9,10,将集合后中的奇数删除,只保留偶数并输出处理后的集合元素

public class TestTwo {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            list.add(i);
        }
        list.stream().filter(num -> num % 2 == 0).forEach(num -> System.out.println(num));
        System.out.println("-------------------------");
        for (Integer i : list) {
            System.out.print(i);
        }
    }

在这里插入图片描述

从以上运行结果可知,虽然流中已经进行了过滤,但是集合中仍是12345678910,并未保存过滤后的元素。

Stream流收集方法

  • Stream类中的方法
方法解释
public R collect()只能收集流中的数据
public R collect(Collector collector)对此流的每个元素执行操作。
public long count()返回此流中的元素个数
  • 工具类Collectors提供了具体的收集方式
方法解释
public static <T> Collector toList()把元素收集到List集合中。底层会创建一个List集合并把所有的数据添加到List集合中
public static <T> Collector toSet()把元素收集到Set集合中(因为Set集合去重,所以在将元素保留到Set集合中时会自动去重)。底层会创建一个Set集合并把所有的数据添加到Set集合中
public static Collector toMap(Function keyMapper,Function valueMapper)把元素收集到Map集合中。底层会创建一个Map集合并把所有的数据添加到Map集合中。注意:keyMapper:一个 Function 函数,用于将流中的元素映射为键。valueMapper:一个 Function 函数,用于将流中的元素映射为值。
public class TestTwo {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        for (int i = 1; i <= 10; i++) {
            list.add(i);
        }
        list.add(10);
        System.out.println("-----------------筛选流中的元素并将筛选后的元素添加到List集合中--------------------");
        List<Integer> list1 = list.stream().filter(num -> num % 2 == 0).collect(Collectors.toList());
        System.out.println(list1);
        System.out.println("-----------------筛选流中的元素并将筛选后的元素添加到Set集合中--------------------");
        Set<Integer> set = list.stream().filter(num -> num % 2 == 0).collect(Collectors.toSet());
        System.out.println(set);
    }
}

在这里插入图片描述

public class TestTwo {
    public static void main(String[] args) {
        HashMap<String, Integer> hm = new HashMap<>();
        hm.put("zhangsan", 23);
        hm.put("lisi", 24);
        hm.put("wangwu", 25);
        hm.put("zhaoliu", 26);
        hm.put("qianqi", 27);
        Map<String, Integer> map= hm.entrySet().stream().filter(s -> s.getValue() >= 25).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
        System.out.println(map);
    }

在这里插入图片描述

注意Map.Entry::getKey属于实例方法引用

如果想要使用接口中的方法但是不想创建接口的实现类,就可以通过方法引用 接口名::方法 的形式,可以直接调用接口中的方法,而不需要显式地创建实现类。这种方式适用于函数式接口,即只包含一个抽象方法的接口。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT机器猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值