欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

DAY18 List Set Map集合

程序员文章站 2022-04-04 08:30:32
...

一、LinkedList集合

java.util.LinkedList集合是java.util.List的实现类,实现List接口的所有方法(添加,删除,查找,判断是空等) ,它添加,删除元素较快,查询相对慢,但是查询头尾元素较快

LinkedList集合实现双向链表接口,实现从头元素到尾元素的链表和从尾到头元素的链表,目标为了增加元素的检索效率 ,如下图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RA9Jq1bR-1604883846569)(E:/%E5%AD%A6%E4%B9%A0%E5%9F%B9%E8%AE%AD/java%E5%9F%BA%E7%A1%80/10.28/%E7%AC%94%E8%AE%B0/assets/%E5%8F%8C%E5%90%91.png)]

关于LinkedList实现大量操作头元素和尾元素的方法。 其中必须通过LinkedList的引用创建该对象

public void addFirst(E e) :将指定元素插入此列表的开头。

public void addLast(E e) :将指定元素添加到此列表的结尾。

public E getFirst() :返回此列表的第一个元素。

public E getLast() :返回此列表的后一个元素。

public E removeFirst() :移除并返回此列表的第一个元素。

public E removeLast() :移除并返回此列表的后一个元素。

public E pop() :从此列表所表示的堆栈处弹出一个元素。

public void push(E e) :将元素推入此列表所表示的堆栈。

public boolean isEmpty() :如果列表不包含元素,则返回true

二、 Set集合

​ java.util.Set 接口 继承自Collection接口,实现对元素的基本操作 ,与java.util.List区别于 Set集合存储无序,且唯一的元素,List存储有序,且可重复的元素

​ Set接口的实现类 HashSet 、 LinekedHashSet 、TreeSet

1、HashSet

​ HashSet集合依据元素的哈希值确定在内存中的存储位置, 所谓Hash值是内存中哈希表的唯一标志,通过哈希值可快速检索到元素所在的位置 , 所以它查询效率高 ,与HashSet类似结构的包括HashMap 等

​ 创建一个HashSet时,就是创建一个HasMap( 关于HashMap结构后面讲)

​ 什么是哈希表?

在Java1.8以前,哈希表的底层实现采用数组+链表结构,但是这样对于“Hash冲突” (两个对象生成的哈希值一样),即多个元素存储在一个“数据桶”中, 这样查找该元素时,依然效率低下, 为了解决由于哈希冲突导致的数据查询效率低下,JDK8以后将哈希表实现采用 数组+链表+红黑树结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n1UWqaJF-1604883846573)(E:/%E5%AD%A6%E4%B9%A0%E5%9F%B9%E8%AE%AD/java%E5%9F%BA%E7%A1%80/10.28/%E7%AC%94%E8%AE%B0/assets/12.png)]

1.2.HashSet存储自定义对象类型

​ HashSet对于对象是否相同的依据,判断对象的hashCode值和equals是否相等,如果它们相等则判断元素一致,不能重复添加

public class People {
    private int pid;
    private String pname;
    private int age;

    public People(int pid, String pname, int age) {
        this.pid = pid;
        this.pname = pname;
        this.age = age;
    }

    public int getPid() {
        return pid;
    }

    public void setPid(int pid) {
        this.pid = pid;
    }

    public String getPname() {
        return pname;
    }

    public void setPname(String pname) {
        this.pname = pname;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public int hashCode() {
        return this.pid;
    }

    @Override
    public boolean equals(Object obj) {
         if(this == obj){
             return true;
         }
         if(obj instanceof  People){
             People p = (People) obj;
             if(p.pid == this.pid && p.getPname().equals(p.getPname())){
                 return true;
             }
         }
         return false;
    }
}
   //存储 对象类型
        Set<People> sets = new HashSet<>();
        People p = new People(1001,"关羽",100);
        People p2 = new People(1002,"张飞",100);
        People p3 = p2;
        System.out.println("p的hashcode:"+p.hashCode());
        sets.add(p);
        // 检查是否为同一个地址
        sets.add(p);

        sets.add(p2);
        sets.add(p3);


        // 插入一个重新new的张飞对象  HashSet以 equals和hashcode的结果作为是否重复对象的依据
        People p4 = new People(1002,"张飞",90);
        sets.add(p4);  //  会当做是重复的对象 ,不能添加成功。

        System.out.println("sets的长度:"+sets.size());
        for(People obj : sets){
            System.out.println(obj.getPid()+"---"+obj.getPname()+"---"+obj.getAge());
        }

1.3 LinkedHashSet

​ 在HashSet中存储的数据是唯一且无序,如何保证数据的有序型,可通过扩展HashSet的子类完成,

java.util.LinkedHashSet ,它实现有序的Hash结构, 它的底层实现使用链表+哈希结构

创建LinkedHashSet时,就是创建一个LinkedHashMap结构 ,linkeHashSet中如何保证顺序一致性

​ accessOrder = false; 按照插入的顺序存储 accessOrder = true: 按照访问的顺序存储。

        // 创建LinkedHashSet对象
        LinkedHashSet<String> set = new LinkedHashSet();
        set.add("aaa");
        set.add("bbb");
        set.add("ccc");
        set.add("ddd");
        //遍历元素
        for(String s : set){
            System.out.println(s);
        }

1.4 TreeSet

TreeSet实现对Set元素的排序功能, 也包含基础的Set集合功能。 存放在TreeSet中的元素时有序的,默认升序,也可以自定义排序规则。

两种方式实现自定义排序规则

1、对元素(自定义类)实现 java.lang.Comparable 接口,重写 compareTo方法

public class Fruit  implements  Comparable<Fruit>{
    private  int id;
    private String name;
    public int compareTo(Fruit o) {
            // return this.id-o.id;  升序
        return  o.id - this.id;
            // 正数: 前一个大于后一个
            // 负数: 前一个小于后一个
    }
 }

        // 实现自定义排序规则的方式一 :  对象实现Comparable接口 (java.lang)
        // 重写compareTo 方法。
        TreeSet<Fruit> fruitSet = new TreeSet<>();
        Fruit f1 = new Fruit(100,"苹果");
        Fruit f2 = new Fruit(101,"香蕉");
        fruitSet.add(f1);
        fruitSet.add(f2);
        System.out.println(fruitSet.size());
        for(Fruit f : fruitSet){
            System.out.println(f.getId()+"---"+f.getName());
        }

2、通过匿名内部类的方式 在创建TreeSet时,创建自定义排序规则 ,new Comparator的接口

  // 自定义排序规则的方式二: 对treeSet实现匿名内部类  new Comparator(java.util)
        TreeSet<Integer> scores  = new TreeSet (new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2-o1;  //降序
            }
        });
        //  添加元素
        scores.add(80);
        scores.add(87);
        scores.add(90);
        scores.add(78);
        for(Integer score :  scores){
            System.out.println(score);
        }

  // 按照对象的某一属性降序
        TreeSet<People> peopleSet = new TreeSet<>(new Comparator<People>() {
            @Override
            public int compare(People o1, People o2) {
                return o2.getAge()- o1.getAge(); // 根据age降序排列
            }
        });
        peopleSet.add(new People(1001,"张飞",100));
        peopleSet.add(new People(1002,"刘备",102));
        peopleSet.add(new People(1003,"关羽",108));

        for(People p : peopleSet){
            System.out.println(p.getPid()+"--"+p.getAge());
        }

三、Map集合

java.util.Map集合用于存储key-value的数据结构 ,一个键对应一个值,其中键在集合中是唯一的, Value可以重复, 例如 学号与学生的关系,省份编号对应省份信息, 对于Map集合的常用实现类包括 HashMap 、LinkedHashMap、HashTable 、TreeMap 等 。

1、HashMap(重点)

​ java.util.HashMap 存储无序的,键值对数据,HashMap的实现原理在JDK1.8以前使用 链表+数组结构,1.8以后使用链表+数组+红黑树结构, 使用Hash表的存储方式其检索效果高

​ 特点:

​ a、HashMap的key唯一,且无序,value不唯一

​ b、HashMap的key和value 都可以为null

​ c、对于相同key 元素,它的value会覆盖原始value

​ HashMap的常用方法

HashMap() 
构造一个空的 HashMap ,默认初始容量(16)和默认负载系数(0.75)。  
HashMap(int initialCapacity) 
构造一个空的 HashMap具有指定的初始容量和默认负载因子(0.75)。  
HashMap(int initialCapacity, float loadFactor) 
构造一个空的 HashMap具有指定的初始容量和负载因子。  
HashMap(Map<? extends K,? extends V> m) 
构造一个新的 HashMap与指定的相同的映射 Map 。  

​ a、put(K key,V value) : 存储key-value 到容器中

​ b、V get(K key): 根据key 获取对应的value

​ c、Set keySet(): 返回所有的key,Set集合

​ d、boolean containsKey(K key): 判断key是否存在

​ e、clear():清空容器的原始

​ f、boolean containsValue(V value):判断value是否存在

​ g、Collection values() : 返回所有的value集合

​ h、isEmpty(): 判断是否为空集合

​ i、remove(Object key) : 根据key删除这个key-value

​ j、size():返回元素的大小

​ k、Set<Map.Entry<Key,Value>> entrySet(): 返回容器的key-value的实体类的集合,方便遍历元素

HashMap的源码分析

   // 创建HashMap对象
        Map<String , Integer> cards = new HashMap();
        //存储
        cards.put("红桃",3);
        cards.put("黑桃",3);
        cards.put("方片",2);
        cards.put("梅花",8);
        cards.put("红桃",2); // 会覆盖原始的value
         //获取指定key的value元素
        System.out.println(cards.get("红桃"));
        // 获取所有的key
        Set<String> keys= cards.keySet();
        //通过遍历所有的key 访问对应的value
        for(String k : keys){
            System.out.println(k+"-----"+cards.get(k));
        }
        // 判断key 是否存在, 判断value是否存在
        System.out.println("是否有红桃:"+cards.containsKey("红桃"));
        System.out.println("判断是否有value:"+cards.containsValue(2));
        // 获取所有的value
        Collection<Integer> values =  cards.values();
        Iterator its= values.iterator();
        while(its.hasNext()){
            System.out.println("values ----"+its.next());
        }

        //获取所有的元素
        System.out.println(cards.size());

        // 遍历map集合元素  entrySet
        Set<Map.Entry<String, Integer>> entrys =   cards.entrySet();
        for(Map.Entry<String, Integer> en  :  entrys ){
            System.out.println("entry遍历方式:"+en.getKey()+"-----"+en.getValue());
        }
        // remove
        System.out.println("删除元素:"+cards.remove("红桃"));
        //清空元素
        cards.clear();
        System.out.println("元素的大小:"+cards.size());
    }

2、HashMap的原理以及源码分析

​ HashMap基于键值对存储,这里讲解的jdk8的源码

​ HashMap实现步骤:数据结构(数组+链表+红核数)

1、根据key生成对应是hash值(采用Hash函数生成) ,根据hash值找到该元素所在的数组结构中的位置,如果该位置中存在元素,说明产生了哈希冲突,此时JDK8采用元素尾插入法 (JDK7采用头插法) ,将元素放入链表的尾部,这样可能会形成一条长长的链表。

​ 2、当链表长度达到8时,此时会转成 红黑树结构(树形结构的检索效率较高) ,为了提高查询效率。

HashMap中的一些关键属性和方法

/**
 * 扩容的临界值,通过capacity * load factor可以计算出来。超过这个值HashMap将进行扩容
 * @serial
 */

int threshold; 
/**
 * 存储键值对的数组,一般是2的幂
 */
transient Node<K,V>[] table;

/**
 * 键值对的实际个数
 */
transient int size;


/**
 * 记录HashMap被修改结构的次数。
 * 修改包括改变键值对的个数或者修改内部结构,比如rehash
 * 这个域被用作HashMap的迭代器的fail-fast机制中(参考ConcurrentModificationException)
 */
transient int modCount;

/**
 * HashMap的节点类型。既是HashMap底层数组的组成元素,又是每个单向链表的组成元素
 */
static class Node<K,V> implements Map.Entry<K,V> {
        //key的哈希值
    final int hash;
    final K key;
    V value;
        //指向下个节点的引用
    Node<K,V> next;
    
}
        //增长因子  0.75 
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
   

几个常用方法分析:

1、get(Object)

 public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    

返回目标Node

final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        // 整个数组长度不为空, 且第一个Node不为空   说明已找到对一个的hash位置
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
            // 表示当前“桶”的第一个元素  的hash值相同,且 key也相同,说明value 是目标查找对象
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            // 说明一个“桶”中有多个元素,  继续找
            if ((e = first.next) != null) {
                // 多个元素中 需要先判断是否为 “树”结构,因为超过8个长度就转成了数
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                // 一定是单链表结构  ,依次从头找到尾,看有没有对应的 key 
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }

2、put(K,V)

    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
     // 获取元素的总长度 
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
       // 判断如果当前集合中没有对应的 “桶”,说明没有出现 “Hash碰撞”
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            
            Node<K,V> e; K k;
             //如果碰撞了,且桶中的第一个节点就匹配了
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            //如果桶中的第一个节点没有匹配上,且桶内为红黑树结构,则调用红黑树对应的方法插入键值对
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                 //不是红黑树结构,那么就肯定是链式结构
                
                for (int binCount = 0; ; ++binCount) {
                    //如果到了链表的尾部 ,插入到后面 
                    if ((e = p.next) == null) {
                        // 创建一个新节点 创建最后节点 next中
                        p.next = newNode(hash, key, value, null);
                        // 如果长度大于 8 
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                             // 将tab 的桶的所有元素 转成  树结构 
                            treeifyBin(tab, hash);
                        break;
                    }
                    // 如果还没有到达尾部 就找到元素了, 直接返回 
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
           // 如果找到该元素 ,需要替换它的  value  
            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;
    }

put插入的流程

DAY18 List Set Map集合

步骤一: 先判断容器是否为空,为空需要扩容,

步骤二: 根据key 生成hash值,根据hash值找到对应的数组的位置,如果数组位置为空,说明没有hash冲突,直接插入,并长度+1 。

步骤三:如果数组位置的内容不为空,说明产生hash冲突 ,继续通过key查找元素,如果第一个元素存在(说明还没有生产链表)直接返回value 并覆盖value的值。

步骤四: 如果key对应的第一个元素不存在,则此时可能出现链表或红黑树, 如果是红黑树采用树结构的插入法(省略分析过程)。 否则一定是链表结构

步骤五: 如果是链表,将该元素插入到末尾,之后验证整个链表的长度是否大于8 ,如果大于8,将链表转成红黑树结构。

步骤六:插入成功之后,判断整个容器的元素个数是否超出 扩容的临界值(threshold = capacity* 增长因子)

关于JDK7.0和JDK8.0的HashMap有什么区别

1、结构不同

​ JDK7采用数组+链表 JDK8采用数组+链表+红黑树

2、hash值的计算方式不同

​ JDK7 table在创建hashmap时分配空间

​ JDK8 通过key的hashcode计算,在put时分配空间

3、发生hash冲突插入方式不同

​ JDK7采用头插法,JDK8采用尾插法

4、resize操作方式不同

​ JDK7重写计算index 的值,JDK8通过判断相应的位是0还是1,要么依旧是原index,要么是oldCap + 原index

3、LinkedHashMap

由于HashMap存储的key是无序,如果需要存储有序的key可使用LinkedHashMap 它依然满足HashMap的所有特点 ,并在此基础上有序


4、TreeMap

TreeMap实现一个可排序的Map集合 ,默认对key升序排列,也可以降序排序,

​ 如果添加元素的key为自定义类,需要实现Comparable接口或Comparator接口。 TreeMap的底层实现是二叉树结构 (有关二叉树的特点 ) ,实现有序的key的分布

 // TreeMap 用于存储可排序的Key -Value集合  ,
    // 其中key必须实现了排序规则的对象 (包装类,String,自定类)
    public static void main(String[] args) {
        // 存储学号和分数   默认对key 进行升序
      //  TreeMap<Integer ,Integer> maps = new TreeMap<>();

        TreeMap<Integer,Integer> maps = new TreeMap<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2-o1;   // 降序
            }
        });

        maps.put(1003,88);
        maps.put(1002,90);
        maps.put(1004,80);
        maps.put(1001,85);
        //输出
        Set<Integer> keys =  maps.keySet();
        for(Integer k : keys){
            System.out.println(k+"----"+maps.get(k));
        }

        // 自定义规则
        // 注意 ,如果key不是包装类而是自定义,必须要求该类实现Comparable或Comparator接口
        TreeMap<Student ,Integer> stuMap = new TreeMap<>();
        stuMap.put(new Student(1001,"张飞"),90);
        stuMap.put(new Student(1003,"刘备"),87);
        stuMap.put(new Student(1002,"关羽"),96);
        // 这里降序排列
        for(Map.Entry<Student,Integer> en : stuMap.entrySet()){
            System.out.println(en.getKey().getSid() + "---"+ en.getValue());
        }
    }

class  Student implements  Comparable<Student>{
    private int sid;
    private String sname;

    public Student(int sid, String sname){
        this.sid = sid;
        this.sname = sname;
    }

    public int getSid() {
        return sid;
    }

    public void setSid(int sid) {
        this.sid = sid;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }

    @Override
    public int compareTo(Student o) {
        return o.sid-this.sid; // this表示前一个对象,  o表示后一个对象
    }
}

5、HashTable

​ HashTable 实现 hash结构的key-value集合, 与HashMap很相似, HashTable 是线程安全(它的很多方法是同步操作),它不需要存储null的 key 和value

​ 扩展自 Dictionary类 和 实现Map接口

常用方法 :

​ put ()

​ get()

​ clear()

​ containsKey()

​ containsValue()

​ 它 有一个子类 是 Properties类,用于存储属性文件的 key- value

 public static void main(String[] args) {
         //创建HashTable   无序
        Hashtable<String,String> tables = new Hashtable<>();
        // 存储
        tables.put("王宝强","马蓉");
        tables.put("贾乃亮","李小璐");
        tables.put("文章","马伊琍");
        //获取   使用所有key遍历  返回枚举类型
        Enumeration<String> keys = tables.keys();
        while(keys.hasMoreElements()){
            String s = keys.nextElement();
            System.out.println(s + "---"+ tables.get(s));
        }

        // 有一个HashTable的子类  Properties
        Properties prop  = new Properties();
        prop.setProperty("username","张三");
        prop.setProperty("password","123456");
        //获取对应属性名的值
        System.out.println("根据属性名获取值:"+prop.getProperty("username"));
        System.out.println("根据属性名获取值:"+prop.getProperty("password"));

    }

四、集合常见面试题

1、Collection 和 Collections 的区别? Array 和Arrays的区别?

​ Collection是集合的*接口 ,Collections是一个集合工具类 ,它提供大量的操作集合方法,例如排序, 打乱顺序,添加元素等。

​ Array 表示一个数组对象 , Arrays是一个数组工具类 ,提供大量的数组操作方法。

2、List 和 Set 的区别? ArrayList 和 LinkedList的区别

​ 相同点:List、Set 都继承Collection接口

​ 不同点: List 存储不唯一,有序集合元素 , Set 存储唯一,无序的集合元素

​ ArrayList 实现数组结构集合,查询比较快,添加,删除效率低

​ LinkedList实现双向链表结构集合,添加,删除效率高, 查询效率低

3、HashSet 和 TreeSet的区别?

它们都存储唯一集合,都实现Set接口

HashSet无序,底层实现Hash结构的存储, TreeSet有序,可实现自定义排序,存储树形结构

4、HashMap 和 HashSet的区别?

它们都属于Hash结构的集合,存储效率较高 , HashSet是存储单个元素 ,HashMap存储键值对元素

HashMap实现Map接口,HashSet实现Set接口 ,

5、HashMap 和HashTable的区别

它们都来自Map 的实现类, HashTable 还继承一个父类 Dictionary

  • HashMap 的key和value 可以为空,HashTable 的key value 不能为空;
  • HashTable 的子类包含key value 的方法
  • HashMap线程不安全效率高 ,HashTable线程安全效率低

6、HashMap 和 TreeMap的区别?

​ 它们都实现Map接口 ,TreeMap 有序,HashMap无序, HashMap实现哈希结构 集合,TreeMap实现二叉树集合。

五、JDK8的特性

在JDK8中新增一些特殊功能,一般开发时方便使用, 其中最主要的功能如下

1、接口的默认函数

public interface MyInterface {

    public default void defaultMethods(){
        System.out.println("这是一个接口的默认方法。");
        // 静态方法可以在default方法中调用
        staticMethods();
    }

    public void service();

    public static void staticMethods(){
        System.out.println("这是接口的静态方法");
    }
}

 public static void main(String[] args) {
        // 创建匿名内部类的方式
        MyInterface my = new MyInterface() {
            @Override
            public void service() {
                System.out.println("service方法.....");
            }
        };
        my.defaultMethods();
        //通过接口名 调用静态方法
        MyInterface.staticMethods();

    }

2、Lambda表达式

​ JDK8中支持一种对方法调用的 简写方式 ,也是一种特殊写法

语法: ([形参名]) ->{ 方法的实现}

这个接口中有且只有一个方法,并对方法实现

原始代码

      ArrayList<String> list = new ArrayList<>();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
        list.add("ddd");
        // 降序
        Collections.sort(list, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o2.compareTo(o1); //降序
            }
        })

由于JDK能识别sort的参数二是Comparator , 并Comparator是函数式接口(一个接口中有且只有一个方法。),实现的一定是唯一的方法名, 所有这里方法名和返回值也省略, 它的简写方式

     // 使用lambda表达式   ([形参])->{方法体}  
        Collections.sort(list ,(String o1,String o2)->{
            return o2.compareTo(o1);
        });

由于形参的数据类与集合的元素类型一致,这里的形参类型也省略 、return也省略

// 最精简的Lambda
        Collections.sort(list ,(o1,o2)->o2.compareTo(o1));

3、函数式接口

函数式接口主要用于满足前面Lambda表达式的语法的使用。 在一个接口中 有且只有一个方法的接口称为 “函数式接口” ,

​ 如何将一个接口定义为函数式接口呢? 在接口上增加注解 “@FunctionalInterface”

package com.j2008.functionalFun;
@FunctionalInterface  // 该注解的意义在于 约束接口只能由一个方法
public interface ConverterInterface<T> {
    public String convertStr(T t);


}
    //传统写法
ConverterInterface<Integer> con = new ConverterInterface<Integer>() {
             @Override
             public String convertStr(Integer o) {
                 return  o.toString();
             }
         };

        String ss =  con.convertStr(100);
        System.out.println(ss);
	//使用 lambda表达式写法
        ConverterInterface<Date> cons = (o)->o.toLocaleString();
        String s =  cons.convertStr(new Date());
        System.out.println(s);

4、方法和构造器的引用

4.1 方法的引用

在以上接口函数中 这个convert方法的实现 对于java.lang包中是存在相同的方法的,所以 convert的实现可以直接引用已有静态方法 Integer.valueOf 或者 Integer.parseInt

​ 以上代码的实现可以改成这样 :

​ 语法: 类名 :: 静态方法名

@FunctionalInterface
public interface Convert<F,T> {
    //将F转成T
    public T convertType (F f);
}
 public static void main(String[] args) {
         // 原始使用lambda表达式 可以这样写
        // 把字符串转成  Integer
        Convert<String ,Integer> con = (f)->{
            return Integer.parseInt(f);
        };
        Integer n = con.convertType("888");

        // 在lambda基础上,如果实现的方法 已存在,则可直接调用  类名::方法名
        Convert<String ,Integer> con2 = Integer::valueOf;
        Convert<String ,Integer> con3= Integer::parseInt;

        //调用方法实现
        Integer n2 = con2.convertType("123");
        int n3 = con3.convertType("555");

 }

4.2 构造器的引用

当方法的实现 是构造器时,可直接引用构造器

语法: 类名::new

@FunctionalInterface
public interface StudentFactory<T> {
    // 参数是创建对象时 的 属性
    public T create(int id ,String name);
}
public class Student {
    private int sid;
    private String sname;
      public Student(int sid, String sname) {
        this.sid = sid;
        this.sname = sname;
    }
  }
 public static void main(String[] args) {
        //使用Lambda实现 函数接口的方法
        StudentFactory<Student> factory = (id,name)->{
            return new Student(id ,name);
        };
        Student stu1 =  factory.create(1001,"张三丰");
        System.out.println(stu1);

        // 以上写法可以直接换成 引用构造器方式
        StudentFactory<Student> factory1 = Student::new;
        //创建
        Student stu2 = factory1.create(1002,"张无忌");
        System.out.println(stu2);
    }

5、集合的流式处理

一、集合的流式处理

在JDK8以后,提供对集合的流式操作,对集合的元素可以向“流水”一样,依次方便,遍历,排序等,它是“不可逆的”(访问后面元素之后不能再次返回前面元素 ) , 根据流的处理方式不同,可以分为 串行流和并行流, 串行流表示同一时间只能有一个流式操作,而并行流可以有多个流式操作。

​ 流返回的结果包括中间操作和 最终操作

中间操作:它的返回值依然是 流对象 ,例如 排序、过滤、去重

最终操作: 返回值是特定的结果类型 ,例如 遍历,取最大值,最小值或返回新的集合

常用方法:

​ stream() :将一个集合流式化

​ filter(): 按条件过滤,里面使用lambda表达式

​ sort(): 排序集合元素

​ distinct: 过滤重复元素

​ reduce() : 将集合的所有元素累加或拼接

​ map(); 映射一个新的集合 对集合元素变更输出

​ collect():返回一个新集合

​ max() min():返回集合最大值或最小值

​ get (): 获取集合计算的结果

 public static void main(String[] args) {
        List<Integer> list = new ArrayList();
        for(int i = 0 ;i<7;i++){
              list.add(i+1);
        }
         // 1、过滤 filter()   过滤掉偶数
        list.stream().filter( param ->param%2==1 )
                    .forEach(System.out::println); //遍历元素

        // 2、排序 sort()   降序
        list.stream().sorted((o1,o2)->o2-o1).forEach(System.out::println);

        //  3 map() 映射一个新的集合 , 如果是奇数 输出奇数 ,否则偶数
        list.stream().map(
                param -> param%2==1?"这个元素是奇数":"这是偶数"
                ).forEach(System.out::println);

        list.add(1);
        list.add(1);
        System.out.println("去重元素");
        // 4 distinct()  去除重复元素
        list.stream().distinct().forEach(System.out::println);

        // 5 reduce() 将集合的所有元素 累加(或拼接)

        int sum =  list.stream().reduce((o1,o2)->o1+o2).get();
        System.out.println("总和:"+sum);

        // 6 collect 返回一个新的集合
        List<Integer> list2= list.stream().filter(param->param%2==1).collect(Collectors.toList());
        System.out.println("遍历新集合");
        list2.stream().forEach(System.out::println);

        // 7、最大和最小
        int max = list2.stream().max((o1,o2)->o1-o2 ).get();
        System.out.println("最大值:"+max);
        int min = list2.stream().min((o1,o2)->o1-o2 ).get();
        System.out.println("最小值:"+min);
    }