精华内容
下载资源
问答
  • 特别要注意的是list集合,我们sql查询返回的list或者stream处理完的list都不可能是null,所以不需要判断是否为null 遍历列表处理 Optional.ofNullable(list) .orElseGet(Lists.newArrayList()) .forEach(...); ...

    1. 判空

    特别要注意的是list集合,我们sql查询返回的list或者stream处理完的list都不可能是null,所以不需要判断是否为null

    • 遍历列表处理
    Optional.ofNullable(list)
            .orElseGet(Array::new)
            .forEach(...);
    
    • 获取对象属性
    String value = Optional.ofNullable(data)
                            .map(data::name)
                            .orElse("");
    

    2. stream

    • 取列表中对象的某个属性组成新的列表
    list2 = list.stream().map(data::name).collect(Collectors.toList());
    
    • 遍历列表,赋值
    list2 = list.stream()
                .map(data -> {
                    data.setName("xx");
                    return data;
                })
                .collect(Collectors.toList());
    
    • 过滤,只要>2的
    list2 = list.stream().filter(data->data.count>2).collect(Collectors.toList());
    
    • list转map
    // 一对多。根据id为key,data为value
    Map<Long, List<data>> map1 = list.stream()
                                     .collect(Collectors
                                     .groupingBy(data::getId));
    // 一对多。根据id为key,data的name为value
    Map<Long, List<String>> map1 = list.stream()
                                     .collect(Collectors
                                     .groupingBy(data::getId, Collectors.mapping(data::getName,Collectors.toList())));
                                     
    // 一对一。根据id为key,data的name为value
    Map<Long, String> mapLevel12 = list.stream()
                                        .collect(Collectors
                                        .toMap(data::getId, data::getName, (key1, key2) -> key2));
    
    • parallelStream使用自定义fork/join池(默认是共用的线程池),分治思想
    ForkJoinPool forkJoinPool = new ForkJoinPool(2);
    List<Long> longList = forkJoinPool.submit(() -> addLevel3ColumnVos.parallelStream()
                                                .map(ComPortletSetColumnReport::getComPortletSetId)
                                                .collect(Collectors.toList()))
                                                .join();
    

    3.function

    函数式编程跟lamdba搭配使用,在参数定义时,我们可以用函数定义,真正调用需要传参时,使用lamdba表达式来传入。

    新版策略模式,利用map+函数式编程取代了大量if/else逻辑

    @Service
    public class BizService {
        @Autowired
        private BizUnitService bizUnitService;
    
        private Map<String, Function<String, String>> checkResultDispatcherComX = new HashMap<>();
    
        /**
         * 初始化 业务逻辑分派Map 其中value 存放的是 lambda表达式
         */
        @PostConstruct
        public void checkResultDispatcherComXInit() {
            checkResultDispatcherComX.put("key_订单1", order -> bizUnitService.bizOne(order));
            checkResultDispatcherComX.put("key_订单1_订单2", order -> bizUnitService.bizTwo(order));
            checkResultDispatcherComX.put("key_订单1_订单2_订单3", order -> bizUnitService.bizThree(order));
        }
        
        public String getCheckResultComX(String order, int level) {
            //写一段生成key的逻辑:
            String ley = getDispatcherComXKey(order, level);
    
            Function<String, String> result = checkResultDispatcherComX.get(ley);
            if (result != null) {
                //执行这段表达式获得String类型的结果
                return result.apply(order);
            }
            return "不在处理的逻辑中返回业务错误";
        }
       
    }
    
    @Service
    public class BizUnitService {
    
        public String bizOne(String order) {
            return order + "各种花式操作1";
        }
        public String bizTwo(String order) {
            return order + "各种花式操作2";
        }
        public String bizThree(String order) {
            return order + "各种花式操作3";
        }
    }
    
    展开全文
  • JDK源码解析---HashSet

    2020-08-15 12:20:36
    判断是否为空8. 判断对象存不存在9. 添加元素10.移除元素11.清空集合 1.概述 内部组合了一个HashMap,所有的操作都是基于HashMap的操作。 因为HashSet存的值对应HashMap中的key,所以是唯一的。 HashSet是无序的。 ...

    1.概述

    内部组合了一个HashMap,所有的操作都是基于HashMap的操作。

    因为HashSet存的值对应HashMap中的key,所以是唯一的。

    HashSet是无序的。

    了解HashSet的源码都是和HashMap相关。底层调用HashMap提供的api

    2.类图

    在这里插入图片描述

    继承了AbstractSet,该类仅实现了3个方法。hashCode、equals、removeAll

    实现了Set接口,定义了一些基本的操作

    实现了序列化接口

    实现了克隆接口

    3. 属性

    private transient HashMap<E,Object> map;//内部组合了一个HashMap,所有的操作都基于HashMap
    private static final Object PRESENT = new Object();//因为HashSet实际存储的就是HashMap中的key,那么value就使用这个常量来替代。
    

    4.构造方法

    4.1 无参构造

    实际就是new了一个HashMap

    public HashSet() {
        map = new HashMap<>();
    }
    

    4.2 带集合参数的构造

    构建一个HashMap 然后将集合C中的元素放入到HashMap中。

    public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }
    

    4.3 初始容量和装载因子的构造

    根据初始容量和装载因子构建HashMap

    public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }
    

    4.4 初始容量的构造

    根据初始容量构建HashMap

    public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }
    

    5. 获得集合迭代器

    public Iterator<E> iterator() {
        return map.keySet().iterator();
    }
    

    6. 返回集合大小

    public int size() {
        return map.size();
    }
    

    7. 判断是否为空

    public boolean isEmpty() {
        return map.isEmpty();
    }
    

    8. 判断对象存不存在

    public boolean contains(Object o) {
        return map.containsKey(o);
    }
    

    9. 添加元素

    所有的键值对 值都是一个常量PRESENT

    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
    

    10.移除元素

    public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }
    

    11.清空集合

    public void clear() {
        map.clear();
    }
    
    展开全文
  • 1.前言按照顺序,本章本是要对Set的相关类进行介绍...2.Map接口除去JDK8添加的新特性不看,Map中定义的方法如下:1.size():返回键值对个数2.isEmpty():判断是否为空3.containsKey(key):键的集合中是否存在所给键...

    1.前言

    按照顺序,本章本是要对Set的相关类进行介绍及讲解的。但是对于其实现有所了解的都应该知道,Set本身的代码十分简单,其实现都是通过Map来实现的,所以本章介绍一下Java中的Map接口及抽象类。

    2.Map接口

    b82dab68e1bfc5c1423f2cc651d874dc.png

    除去JDK8添加的新特性不看,Map中定义的方法如下:

    1.size():返回键值对个数

    2.isEmpty():判断是否为空

    3.containsKey(key):键的集合中是否存在所给键

    4.containsValue(value):值的集合中是否存在所给值

    5.get(key):获取指定键的值

    6.put(key,value):存入一对键值对

    7.remove(key):移除指定键的键值对

    8.putAll(Map):将所给的键值对全部存入

    9.clear():清除所有的键值对

    10.keySet():将所有的键值以Set集合返回

    11.values():返回所有值的集合

    12.entrySet():返回键值对的Set集合

    13.getOrDefault():如果该键存在(哪怕对应值为null),返回其值,否则返回所给默认值

    14.putIfAbsent():如果所给键不存在或者存在对应的值为null,重新设置,否则返回其值

    15.remove(key,value):如果所给的值与该键对应的值不相等就不移除该键。

    16.replace(key, oldValue, newValue):如果键所对应的值与所给旧值相等就用新值替换。

    17.replace(key, value):如果该键存在,就用新值替换。并返回之前的值。

    上述所有方法,从getOrDefault就使用了JDK8的新特性,能在接口中实现默认方法。

    3.Entry

    Entry是map实现的一个关键数据结构(就是一个键值对对象),关于Entry的接口定义如下:

    4c591d25da518792e2b091f54d44ff18.png

    1.getKey():获取键值对的键

    2.getValue():获取键值对的值

    3.setValue():设置键值对的值

    后面的方法都是JDK8相关内容,不进行讲解。

    4.AbstractMap

    抽象类的实现并没有多大参考价值,就像其它集合一样,大部分子类都覆盖了抽象父类的相关方法。

    e76b7e37dbaf7076baec3ae6316aa824.png

    size方法就比较很有意思。其使用的是entrySet的大小,而开篇也说了Set是借助Map实现的,其实际上调用的又是Map的size()方法。如果按抽象父类这种写法,那么就死循环了。同样的还有clear方法。

    9fadcafe53f47b80b9a14d3ec43c2af5.png

    containsValue或者key都是通过entrySet拿到所有的键值对遍历实现的,实现和其它集合的抽象父类基本一致。之后的get,remove等方法都是一样的原理。其余的方法也不再一一叙述,如果前面几章集合的类认真学习过,这个基本是一样的套路。

    展开全文
  • Java HashMap插入分析

    2020-04-21 23:24:03
    首先定义要插入的键值对属于那个桶,定位到桶后,在判断是否为空。如果为空,将键值对存入。如果不为空,将键值对接在链表最后一个位置或者更新键值对。 首先HashMap是变长的集合,所以需要考虑扩容的问题。在JDK8中...

    Java HashMap插入分析

    插入逻辑分析

    首先定义要插入的键值对属于那个桶,定位到桶后,在判断是否为空。如果为空,将键值对存入。如果不为空,将键值对接在链表最后一个位置或者更新键值对。

    首先HashMap是变长的集合,所以需要考虑扩容的问题。在JDK8中,HashMap引入了红黑树优化过长链表,还需要考虑多长的链表需要进行优化。

    • 插入操作源码:
        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;
            // 初始化桶数组 table,table被延迟到插入新数据时再进行初始化
            if ((tab = table) == null || (n = tab.length) == 0)
                n = (tab = resize()).length;
            // 如果桶中不包含键值对节点引用,则将键值对节点的引用存入桶中即可
            if ((p = tab[i = (n - 1) & hash]) == null)
                tab[i] = newNode(hash, key, value, null);
            else {
                Node<K,V> e; K k;
                // 如果键的值以及节点 hash 等于链表中的第一个键值对节点时,将 e 指向该键值对
                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) {
                        // 如果链表长度大于或等于树化阈值,进行树化操作
                        if ((e = p.next) == null) {
                            p.next = newNode(hash, key, value, null);
                            if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                                treeifyBin(tab, hash);
                            break;
                        }
                        // 条件为true,表示当前链表包含要插入的键值对,终止遍历
                        if (e.hash == hash &&
                            ((k = e.key) == key || (key != null && key.equals(k))))
                            break;
                        p = e;
                    }
                }
                // 判断要插入的键值对是否存在HashMap中
                if (e != null) { // existing mapping for key
                    V oldValue = e.value;
                    // onlyIfAbsent 表示是否仅在 oldValue 为 null 的情况下更新键值对的值
                    if (!onlyIfAbsent || oldValue == null)
                        e.value = value;
                    afterNodeAccess(e);
                    return oldValue;
                }
            }
            ++modCount;
            // 键值对数量超过阈值,则进行扩容
            if (++size > threshold)
                resize();
            afterNodeInsertion(evict);
            return null;
        }
    

    插入操作的入口方法是 put(K,V),但核心逻辑在 final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) 方法中。putVal 方法主要做了几件事:

    1. 当桶数组 table 为空,通过扩容的方式初始化table
    2. 查找要插入的键值对是否存在,存在的话根据条件判断是否用新值替换旧值
    3. 如果不存在,则将键值对链入链表中,并根据链表长度决定是否将链表转为红黑树
    4. 判断键值对数量是否大于阈值,大于的话就进行扩容操作

    扩容机制

    HashMap 中,桶数组长度均是2的幂,阈值大小为桶数组长度于负载因子的乘积。当 HashMap 中的键值对数量超过阈值时,进行扩容。

    HashMap 按当前桶数组长度的2倍进行扩容,阈值也变为原来的2倍。扩容之后要重新计算键值对的位置,并把它们移动到合适的位置上去。

    • 扩容过程源码:
    final Node<K,V>[] resize() {
            Node<K,V>[] oldTab = table;
            int oldCap = (oldTab == null) ? 0 : oldTab.length;
            int oldThr = threshold;
            int newCap, newThr = 0;
        	// 如果table为空,表明已经初始化过了
            if (oldCap > 0) {
                // 当table容量超过容量最大值,则不再扩容
                if (oldCap >= MAXIMUM_CAPACITY) {
                    threshold = Integer.MAX_VALUE;
                    return oldTab;
                }
                // 按旧容量和阈值的2倍计算新容量和阈值的大小
                else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                         oldCap >= DEFAULT_INITIAL_CAPACITY)
                    newThr = oldThr << 1; // double threshold
            }
            else if (oldThr > 0) // initial capacity was placed in threshold
                // 初始化时,将threshold的值赋给newCap
                // HashMap使用threshold变量暂时保存initialCapacity参数的值
                newCap = oldThr;
            else {               // zero initial threshold signifies using defaults
                // 调用无参构造方法时,桶数组组容量为默认容量
                // 阈值为默认容量与默认负载因子乘积
                newCap = DEFAULT_INITIAL_CAPACITY;
                newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
            }
        	// newThr为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;
        }
    

    resize()方法做了三件事情:

    1. 计算新桶数组的容量 newCap 和新阈值 newThr
    2. 根据计算出的 newCap 创建新的桶数组,桶数组table也是在这里进行初始化的
    3. 将键值对节点重新映射到新桶数组里,如果节点时TreeNode类型,则需要拆分红黑树。如果是普通节点,则节点按顺序进行分组。
    展开全文
  • 判断是否为空.asp 判断是否为空 判断是否为空值.asp 判断是否为空值 获取变量类型信息.asp 获取变量类型信息 第12章(/11/) 从中截取字符串.asp 从中截取字符串 切分字符串....
  • 判断是否为空.asp 判断是否为空 判断是否为空值.asp 判断是否为空值 获取变量类型信息.asp 获取变量类型信息 第12章(/11/) 从中截取字符串.asp 从中截取字符串 切分字符串....
  • 实例208 检查表单元素的值是否为空 实例209 验证是否为数字 实例210 验证E-mail是否正确 实例211 验证电话号码是否正确 实例212 验证手机号码是否正确 实例213 验证字符串是否为汉字 实例214 验证身份证号码...
  • 实例208 检查表单元素的值是否为空 实例209 验证是否为数字 实例210 验证E-mail是否正确 实例211 验证电话号码是否正确 实例212 验证手机号码是否正确 实例213 验证字符串是否为汉字 实例214 验证身份证号码...
  • 实例028 判断某一年是否为闰年 实例029 验证登录信息的合法性 实例030 为新员工分配部门 实例031 用Switch语句根据消费金额计算折扣 实例032 判断用户输入月份的季节 2.4 循环控制 实例033 使用while与自增...
  • 实例028 判断某一年是否为闰年 实例029 验证登录信息的合法性 实例030 为新员工分配部门 实例031 用Switch语句根据消费金额计算折扣 实例032 判断用户输入月份的季节 2.4 循环控制 实例033 使用while与自增...
  • 实例028 判断某一年是否为闰年 实例029 验证登录信息的合法性 实例030 为新员工分配部门 实例031 用Switch语句根据消费金额计算折扣 实例032 判断用户输入月份的季节 2.4 循环控制 实例033 使用while与自增...
  • 实例028 判断某一年是否为闰年 实例029 验证登录信息的合法性 实例030 为新员工分配部门 实例031 用Switch语句根据消费金额计算折扣 实例032 判断用户输入月份的季节 2.4 循环控制 实例033 使用while与自增...
  • 实例028 判断某一年是否为闰年 实例029 验证登录信息的合法性 实例030 为新员工分配部门 实例031 用Switch语句根据消费金额计算折扣 实例032 判断用户输入月份的季节 2.4 循环控制 实例033 使用while与自增...
  • 实例028 判断某一年是否为闰年 38 实例029 验证登录信息的合法性 39 实例030 为新员工分配部门 40 实例031 用Switch语句根据消费金额计算折扣 41 实例032 判断用户输入月份的季节 42 2.4 循环控制 43 实例033 使用...
  • 实例028 判断某一年是否为闰年 38 实例029 验证登录信息的合法性 39 实例030 为新员工分配部门 40 实例031 用Switch语句根据消费金额计算折扣 41 实例032 判断用户输入月份的季节 42 2.4 循环控制 43 实例033 使用...
  • 实例028 判断某一年是否为闰年 38 实例029 验证登录信息的合法性 39 实例030 为新员工分配部门 40 实例031 用Switch语句根据消费金额计算折扣 41 实例032 判断用户输入月份的季节 42 2.4 循环控制 43 实例033 使用...
  • 实例028 判断某一年是否为闰年 38 实例029 验证登录信息的合法性 39 实例030 为新员工分配部门 40 实例031 用Switch语句根据消费金额计算折扣 41 实例032 判断用户输入月份的季节 42 2.4 循环控制 43 实例033 使用...
  • JAVA面试题最全集

    2010-03-13 13:09:10
    判断一个文件或目录是否存在 如何读写文件 7.Java多态的实现(继承、重载、覆盖) 8.编码转换,怎样实现将GB2312编码的字符串转换ISO-8859-1编码的字符串。 9.Java中访问数据库的步骤,Statement和...
  • java范例开发大全(pdf&源码)

    热门讨论 2013-07-04 13:04:40
    实例14 判断输入的年份是否为闰年 26 实例15 抽奖活动 27 3.2 for语句 28 实例16 小九九乘法表 28 实例17 如何列出素数 29 实例18 Java中的递归 31 实例19 男生女生各多少人 32 实例20 求水仙花数 34 实例21 求任意...
  • Java范例开发大全 (源程序)

    热门讨论 2011-04-27 07:47:22
     实例14 判断输入的年份是否为闰年 26  实例15 抽奖活动 27  3.2 for语句 28  实例16 小九九乘法表 28  实例17 如何列出素数 29  实例18 Java中的递归 31  实例19 男生女生各多少人 32  实例20 求...
  • java范例开发大全源代码

    热门讨论 2011-10-30 23:31:51
     实例14 判断输入的年份是否为闰年 26  实例15 抽奖活动 27  3.2 for语句 28  实例16 小九九乘法表 28  实例17 如何列出素数 29  实例18 Java中的递归 31  实例19 男生女生各多少人 32  实例...
  • java范例开发大全

    2013-03-08 20:06:54
    实例14 判断输入的年份是否为闰年 26 实例15 抽奖活动 27 3.2 for语句 28 实例16 小九九乘法表 28 实例17 如何列出素数 29 实例18 Java中的递归 31 实例19 男生女生各多少人 32 实例20 求水仙花数 34 实例21 求任意...
  • Java范例开发大全(全书源程序)

    热门讨论 2013-04-05 11:50:26
    实例14 判断输入的年份是否为闰年 26 实例15 抽奖活动 27 3.2 for语句 28 实例16 小九九乘法表 28 实例17 如何列出素数 29 实例18 Java中的递归 31 实例19 男生女生各多少人 32 实例20 求水仙花数 34 实例21...
  • 1.2.4 测试环境是否安装成功 8 1.2.5 如果失败了怎么办? 9 1.3 让自己的第一个程序运行起来 10 1.3.1 编写自己的Hello World源程序 10 1.3.2 编译自己的HelloWorld程序 11 1.3.3 让代码运行起来 13 1.4 初探...
  • 1.2.4 测试环境是否安装成功 8 1.2.5 如果失败了怎么办? 9 1.3 让自己的第一个程序运行起来 10 1.3.1 编写自己的Hello World源程序 10 1.3.2 编译自己的HelloWorld程序 11 1.3.3 让代码运行起来 13 1.4 初探...
  • 1.2.4 测试环境是否安装成功 8 1.2.5 如果失败了怎么办? 9 1.3 让自己的第一个程序运行起来 10 1.3.1 编写自己的Hello World源程序 10 1.3.2 编译自己的HelloWorld程序 11 1.3.3 让代码运行起来 13 1.4 初探...
  • f = ChannelUtil.syncWriteLongMsgToEntity([clientEntityId],message)方法发送,要判断f是否为Null,为Null表示发送失败,一条短信可能拆分成多条,因此返回List。 如何发送长短信? smsgate默认已经处理好长短信...
  • 自定义RecyclerView下拉刷新上拉加载,支持加载loading,页面,异常界面,有数据界面状态切换 缓存使用Realm数据库,做数据的增删改查 状态管理库与Activity和Fragment结合,可以自由切换不同的状态 3.3 项目...

空空如也

空空如也

1 2 3
收藏数 41
精华内容 16
关键字:

判断集合是否为空jdk8