2021-06-25

集合源码解读

集合体系图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-baIB2EiL-1624602573916)(E:\Typora\QQ截图20210624120203.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nWk0RReo-1624602573917)(E:\Typora\QQ截图20210624120223.png)]

集合主要是两组,单列集合Collection和双列集合Map

集合实现类总结

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KXvTWNQ9-1624602573919)(E:\Typora\QQ截图20210624212653.png)]

Hash Set源码解读

public class HashSetSource {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        hashSet.add("java");
        hashSet.add("php");
        hashSet.add("java");
        System.out.println("set="+hashSet);
    }
}

hashset图片说明

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0TigK1gB-1624602573921)(E:\Typora\QQ截图20210622090808.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y8UxPKPC-1624602573922)(E:\Typora\QQ截图20210622094249.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f4li0XiJ-1624602573924)(E:\Typora\QQ截图20210622144311.png)]

###构造方法

    public HashSet() {
        map = new HashMap<>();  //底层是一个hashmap
    }

add方法

public boolean add(E e) { //e是添加的元素,例如Java
    return map.put(e, PRESENT)==null;
}

idea快捷键F4:在当前类中查找变量的来源,例如上述代码的PRESENT

private static final Object PRESENT = new Object();
//PRESENT只是一个对象,无法改变(static final),相当于占位符,占据put方法的value
put方法
public V put(K key, V value) { //key="java",value="PRESENT"
    return putVal(hash(key), key, value, false, true);//到了这个代码块不能点step into,会直接进入putVal,先进入hash(key),应该点击force step into,
}
hash值方法
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    //hashCode方法是object类方法,可以重写该方法,为一个object获得一个hash值
}
putVal方法
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是hashmap的属性,是放node节点的数组,类型是Node[]
    if ((tab = table) == null || (n = tab.length) == 0)//如果table为空,resize为他分配默认的16个空间大小
        n = (tab = resize()).length;
    if ((p = tab[i = (n - 1) & hash]) == null)
        //根据hash值得到key应该存放在table表的哪个索引位置,并且把这个位置的对象赋值给p,p是该hash值的第一个元素
        //为空的话,表示没有存放元素,就创建一个node(key,value)
        //如果有相同的hash值了,得到的table索引位置就不为空。 
        tab[i] = newNode(hash, key, value, null);
    else {
        Node<K,V> e; K k;//一个开发技巧:在需要局部变量(辅助变量)的时候,再创建
        if (p.hash == hash &&//如果当前索引位置对应的链表的第一个元素和准备添加的key的hash值一样,并且满足下面两个条件之一:    
            //(1)准备加入的key和p指向的node节点的key是同一个对象
            //(2)p指向的node节点的key的equals()和准备加入的key比较后相同(针对对象比较),注意equals方法可以重写,自己定义规则。
            ((k = p.key) == key || (key != null && key.equals(k))))
            e = p;
        else if (p instanceof TreeNode)//判断p是不是红黑树,是的话调用putTreeVal进行添加
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {//如果table对应索引位置,已经是一个链表,就使用for循环比较
            //(1)依次和链表的每一个元素比较后,都不相同,则加入链表
            //(2)如果有相同,则break
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {//索引位置只有一个元素,直接放到后面
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) 
                        // 把元素添加到链表后,立即判断该链表是否达到8个结点
                        //达到就调用treeifyBin,对当前链表树化
                        //注意,在转成红黑树前,要进行判断,判断条件:
                        //if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY(64))
                        //             resize();
                        //条件成立,先table扩容至64,条件不成立,才进行红黑树化。                      
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;//相同了直接break
                p = e;//指向下一个
            }
        }
        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)//threshold表示16乘加载因子0.75
        //size就是我们每加入一个结点node,size就会++,不管是加入链表还是table
        resize();
    afterNodeInsertion(evict);//为hashmap子类存在的方法,于该add方法无关
    return null;//返回空代表成功
}

注意

如果我们要求对象的属性相同,则返回相同的hash值,我们应该重写hashcode方法

LinkedHashSet源码解读

public class LinkedHashSetSource {
    public static void main(String[] args) {
        LinkedHashSet linkedHashSet = new LinkedHashSet();
        linkedHashSet.add("AA");
        linkedHashSet.add(456);
        linkedHashSet.add(456);
        linkedHashSet.add(123);
        linkedHashSet.add("HSP");
        System.out.println("linkedHashSet"+linkedHashSet);
    }
}

输出linkedHashSet[AA, 456, 123, HSP]

LinkedHashSet加入顺序和取出顺序一致

构造方法

HashSet(int initialCapacity, float loadFactor, boolean dummy) {
    map = new LinkedHashMap<>(initialCapacity, loadFactor);
}

linkedHashSet底层是一个LinkedHashMap(hashmap的子类)

底层结构(数组table和 双向链表)

添加第一次时,直接将数组table扩容到16,数组是HashMap N o d e [ ] 类 型 , 存 放 元 素 类 型 是 L i n k e d H a s h M a p Node[]类型,存放元素类型是LinkedHashMap Node[]LinkedHashMapEntry

static class Entry<K,V> extends HashMap.Node<K,V> {
    Entry<K,V> before, after;
    Entry(int hash, K key, V value, Node<K,V> next) {
        super(hash, key, value, next);
    }
}

LinkedHashSet图片说明

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ofw044sV-1624602573924)(E:\Typora\QQ截图20210623163644.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-49AcV5d5-1624602573925)(E:\Typora\QQ截图20210623165315.png)]

add方法

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

add方法和hashset一样。

HashMap源码解读

public class CollectionFor {
    public static void main(String[] args) {
        HashMap map = new HashMap();
        map.put("no1","饶冬章");
        map.put("no2","张无忌");
        map.put("no1","张三丰");
        System.out.println("map="+map);
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QgQ02mjB-1624602573926)(E:\Typora\QQ截图20210624151344-1624519915560.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5DeLYKcO-1624602573927)(E:\Typora\QQ截图20210624163438.png)]

key如果重复了,会替换上一次的值

k-v 最后是HashMap$Node node = newNode(hash, key, value, null)

k-v为了方便程序员的遍历,还会创建EntrySet 集合,该集合存放的元素类型 Entry,而一个Entry对象就有k,v EntrySet<Entry<K,V>>

static class Node<K,V> implements Map.Entry<K,V> {
    final int hash;
    final K key;
    V value;
    Node<K,V> next;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D1NoshFF-1624602573927)(E:\Typora\QQ截图20210624153348.png)]

public Set<Map.Entry<K,V>> entrySet() {
    Set<Map.Entry<K,V>> es;
    return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}

entrySet方法返回的类型是Set<Map.Entry<K,V>>,要想得到Map.Entry<K,V>需要向下转型。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-380SrYJQ-1624602573928)(E:\Typora\QQ截图20210624171635.png)]

扩容机制

和hashset一样

HashTable解读

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DVh7wdAW-1624602573928)(E:\Typora\QQ截图20210624195441.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-42i3052A-1624602573929)(E:\Typora\QQ截图20210624210753.png)]

put方法

public synchronized V put(K key, V value) {
    // Make sure the value is not null
    if (value == null) {
        throw new NullPointerException();
    }

    // Makes sure the key is not already in the hashtable.
    Entry<?,?> tab[] = table;
    int hash = key.hashCode();
    int index = (hash & 0x7FFFFFFF) % tab.length;
    @SuppressWarnings("unchecked")
    Entry<K,V> entry = (Entry<K,V>)tab[index];
    for(; entry != null ; entry = entry.next) {
        if ((entry.hash == hash) && entry.key.equals(key)) {
            V old = entry.value;
            entry.value = value;
            return old;
        }
    }

    addEntry(hash, key, value, index);
    return null;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RPsUlnAn-1624602573930)(E:\Typora\QQ截图20210624210912-1624542680568.png)]

扩容机制

private void addEntry(int hash, K key, V value, int index) {
    modCount++;

    Entry<?,?> tab[] = table;
    if (count >= threshold) {
        // Rehash the table if the threshold is exceeded
        rehash();

        tab = table;
        hash = key.hashCode();
        index = (hash & 0x7FFFFFFF) % tab.length;
    }

    // Creates the new entry.
    @SuppressWarnings("unchecked")
    Entry<K,V> e = (Entry<K,V>) tab[index];
    tab[index] = new Entry<>(hash, key, value, e);
    count++;
}

Treeset源码解读

public class TreeSetSource {
    public static void main(String[] args) {
        //TreeSet treeSet = new TreeSet
        TreeSet treeSet = new TreeSet(new Comparator(){
            @Override
            public int compare(Object o1, Object o2) {
                //下面调用String的compareTO方法进行字符串大小的比较
                return ((String) o1).compareTo((String) o2);
            }
        });
        treeSet.add("jack");
        treeSet.add("tom");
        treeSet.add("sp");
        treeSet.add("a");
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nj0G4mZj-1624602573930)(E:\Typora\QQ截图20210624220350.png)]

public TreeMap(Comparator<? super K> comparator) {
    this.comparator = comparator;
}
public TreeSet(Comparator<? super E> comparator) {
        this(new TreeMap<>(comparator));
    }

构造器把传入的比较器对象赋给了TreeSet的底层的 TreeMap的属性this.comparator

treeSet底层是treeMap

public V put(K key, V value) {
    Entry<K,V> t = root;//该树的根节点
    if (t == null) {
        compare(key, key); // type (and possibly null) check,检测是否为空

        root = new Entry<>(key, value, null);
        size = 1;
        modCount++;
        return null;
    }
    int cmp;
    Entry<K,V> parent;
    // split comparator and comparable paths
    Comparator<? super K> cpr = comparator;//cpr就是我们的匿名内部类(对象)
    if (cpr != null) {
        do {//一直循环直到找到插入位置
            parent = t;
            cmp = cpr.compare(key, t.key);//动态绑定到我们的匿名内部类compare,cmp是在比较规则的情况下之间的差值,比如字符串ascii码之间的差值
            if (cmp < 0)
                t = t.left;//在根节点的左边
            else if (cmp > 0)
                t = t.right;
            else//如果相等,即返回0,这个key就没有加入
                return t.setValue(value);
        } while (t != null);//t为空退出,找到了要插入的位置
    }
    else {//构造器为空,分析和上面一样
            if (key == null)
                throw new NullPointerException();
            @SuppressWarnings("unchecked")
                Comparable<? super K> k = (Comparable<? super K>) key;
            do {  
                parent = t;
                cmp = k.compareTo(t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        Entry<K,V> e = new Entry<>(key, value, parent);//要插入的位置在parent的左边或者右边
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用python中的pymsql完成如下:表结构与数据创建 1. 建立 `users` 表和 `orders` 表。 `users` 表有用户ID、用户名、年龄字段,(id,name,age) `orders` 表有订单ID、订单日期、订单金额,用户id字段。(id,order_date,amount,user_id) 2 两表的id作为主键,`orders` 表用户id为users的外键 3 插入数据 `users` (1, '张三', 18), (2, '李四', 20), (3, '王五', 22), (4, '赵六', 25), (5, '钱七', 28); `orders` (1, '2021-09-01', 500, 1), (2, '2021-09-02', 1000, 2), (3, '2021-09-03', 600, 3), (4, '2021-09-04', 800, 4), (5, '2021-09-05', 1500, 5), (6, '2021-09-06', 1200, 3), (7, '2021-09-07', 2000, 1), (8, '2021-09-08', 300, 2), (9, '2021-09-09', 700, 5), (10, '2021-09-10', 900, 4); 查询语句 1. 查询订单总金额 2. 查询所有用户的平均年龄,并将结果四舍五入保留两位小数。 3. 查询订单总数最多的用户的姓名和订单总数。 4. 查询所有不重复的年龄。 5. 查询订单日期在2021年9月1日至9月4日之间的订单总金额。 6. 查询年龄不大于25岁的用户的订单数量,并按照降序排序。 7. 查询订单总金额排名前3的用户的姓名和订单总金额。 8. 查询订单总金额最大的用户的姓名和订单总金额。 9. 查询订单总金额最小的用户的姓名和订单总金额。 10. 查询所有名字中含有“李”的用户,按照名字升序排序。 11. 查询所有年龄大于20岁的用户,按照年龄降序排序,并只显示前5条记录。 12. 查询每个用户的订单数量和订单总金额,并按照总金额降序排序。
06-03

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值