【Day12-Stream流&Map集合】

一、Stream流

是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。

“集合讲的是数据,流讲的是计算!”

Stream 自己不会存储元素,Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

一般开发的套路是:创建一个Stream:一个数据源(数组、集合)-->中间操作:一个中间操作,处理数据源数据-->终止操作:一个终止操作,执行中间操作链,产生结果。

1、JDK1.8中的函数式接口与流

在jdk1.8中有一个包 java.util.function,存放的都是函数式接口这些函数式接口,大致分为下图几中类型类:

 1、函数型接口,一个参数,一个返回值

//R apply(T t);

Function<String,Integer> function = t ->{return t.length();};

System.out.println(function.apply("abcd"));

2、断定型接口,一个参数,返回boolean

//boolean test(T t);

Predicate<String> predicate = t->{return t.startsWith("a");};

System.out.println(predicate.test("a"));

3、消费型接口,一个参数,没有返回值

// void accept(T t);

Consumer<String> consumer = t->{

System.out.println(t);

};

consumer.accept("javaXXXX");

4、供给型接口,无参数,有返回值

//T get(); 

Supplier<String> supplier =()->{return UUID.randomUUID().toString();};

System.out.println(supplier.get());
package day12;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 请按照给出数据,找出
 *  * 偶数ID
 *  * 年龄大于24
 *  * 用户名转为大写
 *  * 用户名字母倒排序
 *  * 只输出2个
 *  //首先我们需要一个数据源  集合或者数组
 *         List<User> list =new ArrayList<User>();
 *         list.add(new User(11,"zhangsan",25));
 *         list.add(new User(12,"lisi",25));
 *         list.add(new User(13,"wangwu",35));
 *         list.add(new User(14,"zhaoliu",15));
 *         list.add(new User(15,"tanxin",38));
 *         list.add(new User(16,"wang",45));
 */
public class TestStream {

    public static void main(String[] args) {

        List<User> list =new ArrayList<User>();
        list.add(new User(11,"zhangsan",25));
        list.add(new User(12,"lisi",25));
        list.add(new User(13,"wangwu",35));
        list.add(new User(14,"zhaoliu",15));
        list.add(new User(15,"tanxin",38));
        list.add(new User(16,"wang",45));


        /**
         *   使用stream流
         */
        Stream<User> stream = list.stream();
        /**
         * 可以通过工具类获取数组的Stream流
         *String[] arr = {"张三"};
         * Stream<String> stream1 = Arrays.stream(arr);
         */


        /**
         *  过滤 filter
         *   返回true 表示保留,返回false 代表过滤
         *  map
         *    将集合中的元素一个个传递进来,然后经过处理再返回回去
         *  limit
         *    限定输出的个数
         *  foreach
         *    对集合中的元素循环遍历。
         *    sorted
         *       排序,里面可以放入一个比较器
         *   collect
         *      从一个集合中获取一个Stream流,通过流进行数据的处理,处理完之后,我们实际的集 
         *      合里面的元素是不会发生改变的。
         *      我们如果想将处理完的流Stream对象变为list 集合,需要使用到 collect
         *      收集的意思:  可以收集成list ,  set
         *      该方法是流对象的方法,不能在forEach 方法后调用。
         */

        List<User> userList = stream.filter(user -> {
            if (user.getId() % 2 == 0) {
                return true;
            }
            return false;
        }).filter(user -> {
            if (user.getAge() > 24) {
                return true;
            }
            return false;
        }).map(user -> {
            String daXie = user.getName().toUpperCase();
            StringBuffer stringBuffer = new StringBuffer(daXie);
            System.out.println(stringBuffer.toString());
            user.setName(stringBuffer.reverse().toString());
            return user;
        }).sorted((u1, u2) ->
                //u1.getAge() - u2.getAge()
                u1.getName().compareTo(u2.getName())
        ).limit(2).collect(Collectors.toList());/*forEach(user->{
            System.out.println(user);
        })*/
        System.out.println(userList);

    }
}

二、案例(Stream流)

问题:

/**
* 请按照给出数据,找出
* 偶数ID
* 年龄大于24
* 用户名转为大写
* 用户名字母倒排序
* 只输出一个
* 用户名字
*/

举例:

package day12;

public class User {

    private int id;
    private String name;
    private int age;

    public User(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
package day12;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Stream;

/*
 * 请按照给出数据,找出
 * 偶数ID
 * 年龄大于24
 * 用户名转为大写
 * 用户名字母倒排序
 * 只输出2个
 *
 *   Stream   流  很重要,因为现在很多地方都这么写了,一定要重视一下
 *     常用的方法:
 *        filter  接收的是断定型接口
 *        map     接收的是函数型接口
 *        sorted   接收一个比较器
 *        forEach  接收一个消费性接口
 *        limit  表示显示多少行
 *
 */
public class TestStreamMain {

    public static void main(String[] args) {
        //首先我们需要一个数据源  集合或者数组
        List<User> list =new ArrayList<User>();
        list.add(new User(11,"zhangsan",25));
        list.add(new User(12,"lisi",25));
        list.add(new User(13,"wangwu",35));
        list.add(new User(14,"zhaoliu",15));
        list.add(new User(15,"tanxin",38));
        list.add(new User(16,"wang",45));

        Stream<User> stream = list.stream();
        // filter 过滤   里面需要接收一个断定型接口
        // 因为是对list集合进行遍历,所以p指的就是一个个的user
        stream.filter( p-> {
            if(p.getId()%2==0){
                return true;
            }
            return false;
            // filter 可以用来过滤数据
        }).filter( p->{
            if(p.getAge()>24){
                return true;
            }
            return false;
            // map 可以用来修改数据
        }).map( p->{
            p.setName(p.getName().toUpperCase());
            return p;
            // sorted 对数据按照一定规则排序,里面使用到了比较器这样一个接口
        }).sorted((user1, user2) -> {
            return user2.getName().compareTo(user1.getName());
        }).limit(1)
        .forEach(System.out::println); //等同于如下操作
        /*forEach((t)->{
            System.out.println(t);
        })*/;

    }
}

二、Map集合

1、Map集合的常用方法

 map集合的常用方法

       /**
         *   Map 是一个接口,父接口不是Collection
         *   Map 接口他的有名的实现类 HashMap
         *   他跟之前的集合相比,最大的不同就是 是以键值对形式出现的。
         *   Map 集合又称之为本地缓存(jvm内存中的)。
         */

    Map<String, String> map = new HashMap<>();

 1、put 存放数据, 而且有返回值,如果key已经存在,就覆盖掉原来的value,并且返回被覆盖掉的 
    值,如果不是覆盖,返回null
    map.put("100","小二");
    map.put("200","小四");
    map.put("300","小三");       
 2、get 通过key 值获取 value
    System.out.println(map.get("100"));

    如果出现相同的key值,会覆盖原来的数据
    String name = map.put("100", "王德法");
    System.out.println(name);
 
    null可以当做map集合中的key 和 value
    map.put(null,"路遥");
    map.put("400",null);
    System.out.println(map);
    System.out.println(map.get(null));

    put是覆盖,putIfAbsent是存在就插入不了了,不是覆盖,而且返回已经存在的值
    String name2 = map.putIfAbsent("300", "无名"); //打印出来的是小三 
    System.out.println(name2);


     HashMap<String, String> m = new HashMap<>();
       m.put("176","郑局");
       m.put("184","王总");
       m.put("188","张局");
       map.putAll(m);
       System.out.println(map);


 3、remove  删除键值对
    map.remove("176");           //通过Key  删除键值对
    map.remove("188","张局");  // 通过key 和 value 删除键值对
    System.out.println(map);

 4、map.clear();    清空集合。

 5、replace(key, oldValue, newValue)  //只有当key和oldValue是匹配的
                                      //情况下,才会将值修改成newValue。
    boolean b = map.replace("184","王总","赵厅");  //返回值为boolean
    System.out.println(map);

 6、replaceAll(BiFunction<K,V,V> biFunction) //对集合中的元素进⾏批量的替换
                                               将集合中的每⼀个键值对,带⼊
                                               到BiFunction的⽅法中, 使⽤接⼝
                                               ⽅法的返回值替换集合中原来的值。
    
    map.replaceAll(new BiFunction<String, String, String>() {
            @Override
            public String apply(String key, String value) {
                System.out.println("k="+key);
                System.out.println("v="+value);
                if(null != key && Integer.valueOf(key) > 300){
                    return "hello" + value;
                }else{
                    return "world";
                }

            }
         });

 7、get(K key)
    //通过键, 获取值。 如果键不存在, 返回null。  

 8、getOrDefault(K key)
    //通过键, 获取值。 如果键不存在, 返回默认的值。
    
 9、size()
    //获取集合中的元素数量(有多少个键值对)
 
10、isEmpty()
    //判断集合是否为空

11、containsKey(K key)
    //判断是否包含指定的键
    containsValue(V value)
    //判断是否包含指定的值

12、keySet()
    // 获取由所有的键组成的集合(因为键是不允许重复的,因此这⾥返回的是Set集合)

13、values()
    //获取由所有的值组成的集合

2、Map集合的三种遍历方式

HashMap<String, String> m = new HashMap<>();
       m.put("176","郑局");
       m.put("184","王总");
       m.put("188","张局");

//第一种,获取里面所有的键值对,通过key 获取value

      Set<String> keys = m.keySet();
          for (String key:keys) {
              System.out.println("Key="+key+",value ="+m.get(key));
      }

//第二种, forEach获取
       m.forEach(new BiConsumer<String, String>() {
            @Override
           public void accept(String key, String value) {
               System.out.println("Key="+key+",value ="+m.get(key));
           }
       });

       //Lambda表达式输出
       m.forEach((key,value) -> System.out.println("Key="+key+",value ="+m.get(key)));

// 第三种:使用Entry 获取 key value值

       Set<Map.Entry<String, String>> entrySet = m.entrySet();
            for (Map.Entry<String,String> entry:entrySet) {
                 System.out.println("Key="+entry.getKey()+",value ="+entry.getValue());
            }

3、Hashtable

HashMap与Hashtable两者对比:

1. HashMap是线程不安全的集合, Hashtable是线程安全的集合。
2. HashMap允许出现null键值, Hashtable是不允许的。
3. HashMap的父类是AbstractMap, Hashtable的父类是Dictionary。
4. HashMap的Map接口的新的实现类, 底层算法效率优于Hashtable。
 

4、TreeMap

原理实现:

与TreeSet⼀样,进⾏排列,只是TreeMap是按照键的⼤⼩实现,对于值是不管的。我们可以将TreeSet中的值理解成TreeMap中的键。

TreeMap 是有序的,指的是Key有序。

5、LinkedHashMap

HashMap  -->  LinkedHashMap
HashMap 中存储了多个元素,打印输出的时候,不按照当时存入的顺序打印。
LinkedHashMap 打印就是按照存储的顺序来的。

6、HashMap的存储原理

HashSet 底层数据结构是哈希表

JDK1.8之前结构:数组+链表

JDK8里面:数组+链表+红黑树

       JDK1.8之后结构:节点数少于8个的时候采用数组+链表的结构,当节点数多于8个的时候,采用的是数组+红黑树的结构 。

        HashMap存储的数据存放在内存中,提高HashMap数据寻址速度是重点要解决的问题,所以HashMap底层的存储结构非常关键,如果使用数组存储,时间复杂度为O(1),使用链表存储,时间复杂度为O(n),如果使用二叉树存储,时间复杂度为O(lg(n))。所以HashMap优先使用数组存储,如果出现hash碰撞,采用链表存储,如果链表长度大于8,寻址速度明显下降,进一步采用红黑树存储,将寻址效率提高。

        当存放两个元素获取Hash并且Mod 16得到Hash桶编号相同时,出现Hash碰撞,hash桶使用链表存储这两个对象,使用HashMap.get(key)获取这两个对象时,需要两步实现,首先得到它们对应的Hash桶号,在根据Key与链表中的Key1=Value1比较key.equale(key1),找出对应元素,这样需要递归遍历列表中的元素,执行效率降低。当链表长度大于8时,HashMap将链表存储转化为红黑树存储,红黑树遍历每次可以减少一个分支,效率比链表高,其时间复杂度为O(lg(n))。

7、  HashMap 的扩容规则

       HashMap使用数组存储,默认长度为16,加载因子为0.75,当HashMap的Hash桶数量的75%被分配使用时,HashMap扩容为原来的两倍。 跟HashSet一样。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值