集合、ArrayList、HashSet(源码)第二部分(Set、Map)

7.1Set接口的基本介绍

1)无序(添加和取出的顺序不一致),没有索引。

2)不允许重复元素,即最多包含一个null。

3)Set接口常用的实现类有TreeSet、HashMap。

Set是Collection的子接口,所以常用的方法与Collection接口一致。

Set set = new HashSet();
set.add("lutao"); 
set.add("lutao1"); 
set.add("lutao2"); 
set.add("lutao3"); 
set.remove("lutao"); 
set.size();//获取长度 //等等方法

8.1HashSet类的特点

1)HashSet实现的是Set接口即(无序、元素不可重复)

2)HashSet底层实际是HashMap

3)HashMap的底层结构是(数组+链表+红黑树)

底层是new了一个HashMap

机制

package com.lt.Set_;

import java.util.HashMap;
import java.util.HashSet;

@SuppressWarnings({"all"})
public class HashSetSource {
    public static void main(String[] args) {
        HashSet hashSet = new HashSet();
        hashSet.add("Java");
        hashSet.add("Java");
        System.out.println(hashSet);

        //1.执行add()
         public boolean add(E e) {
                return map.put(e, PRESENT)==null; final PRESENT = new Object();
            }                    //在HashSet中是一个固定的默认值,在Map中才起作用。
        //2.执行put(K key, V value) key是添加的元素,value是固定值
        public V put(K key, V value) {
            return putVal(hash(key), key, value, false, true);
        }
        //3.执行hash(Object key)          得到的hash值决定该元素在table的位置
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16)(无符号右移16位);
        //4.执行putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict)
        HashMap.Node<K,V>[] tab; HashMap.Node<K,V> p; int n, i;
        //table 就是HashMap 的一个数组,类型是Node[]
        //判断该tab是否为null,如果是则进行第一次扩容  扩容到 16.
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        //判断该元素存入的位置是否已经存在元素,如果不存在,则直接将newNode(hash, key, value, null)存入该位置
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            HashMap.Node<K,V> e; K k;
        //如果链表的第一个索引位置的元素与即将加入的元素hash值相同,
        // 并且即将加入的key与p指向的Node结点的key是同一个对象,
        // 或者它们两者调用的equals比较后相同,则不能添加。
            if (p.hash == hash &&
                    ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
        //判断 p是否是一颗红黑树,如果是则调用PutTreeVal添加
            else if (p instanceof HashMap.TreeNode)
                e = ((HashMap.TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
        //如果该key加入的位置已经存在一条链表则循环比较每一个元素,如果都不相同,则将key添加到链表最后。
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
        //如果该条链表的已经存在8个结点了,那么则调用treeifBin(tab.hash)判断是否达到转成红黑树的条件。
        //                ①即单条链表长度已经到达8,②并且table的大小>=64。
        //                 如果第一个条件成立,第二个条件不成立,则先将table扩容
                        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;
        //首先e = p.next , p = e, 比较完当前元素后,重新指向下一个元素
                    p = e;
                }
            }
        //如果key重复,才会进入到这里
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount; 记录添加次数
        //每次添加完后 size+1.
        //当临界值为12时,如果table的长度已经达到12,再次添加一个元素的话,则需要调用resize()扩容。
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
        //执行resize()扩容
    }
}

9.1LinkedHashSet类的说明

1)LinkedHashSet是HashSet的子类;

2)LinkedHashSet底层是一个LinkedHashMap,底层维护的是数组+双链表的结构;

3)LinkedHashSet跟HashSet一样根据哈希值运算得到在数组的位置,但底层的次序是由双向链表的前后缀维护元素的次序,所以元素是有序的。

LinkedHashSet添加元素的底层源码与HashSet相差无几,就不再解读了。

1)在LinkedHashSet中维护了一个hash表和双向链表

(维护了head和tail两个属性,分别指向链表的头尾。)

2)每一个结点有一个before和after,分别指向结点的前后缀,形成一个双向链表

3)添加元素的规则与HashSet一致。

9.2扩展

重写equals 和hashCode

判断当name和price两者都相同的情况下,才被认为是相同的元素。

package com.lt.Set_;

import java.util.LinkedHashSet;
import java.util.Objects;

public class LinkedHashSetExrecise {
    public static void main(String[] args) {
        LinkedHashSet linkedHashSet = new LinkedHashSet();
        linkedHashSet.add(new Car("奥拓",1000));
        linkedHashSet.add(new Car("奥迪",300000));
        linkedHashSet.add(new Car("法拉利",1000000000));
        linkedHashSet.add(new Car("奥迪",300000)); //name 和price 都与前面相同,添加失败
        linkedHashSet.add(new Car("保时捷",700000000));
        linkedHashSet.add(new Car("奥迪",300000));
        for (Object o : linkedHashSet) {
            System.out.println(o);
        }

    }
}
class Car{
    private String name;
    private double price;

    public Car(String name, double price) {
        this.name = name;
        this.price = price;
     @@@省略get set和toString方法
    //重写equals 和hashCode
    //判断当name和price两者都相同的情况下,才被认为是相同的元素。
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Car)) return false;
        Car car = (Car) o;
        return Double.compare(car.price, price) == 0 && Objects.equals(name, car.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, price);
    }
}

10.1Map 接口实现类的特点

1)Map和Collection是并列,即两者之间没有关系。

2)Map用于保存具有一对一映射关系的数据:key-value

3)Map中的key和value任意的引用类型(即Object),封装到HashMap$Node对象中

4)Map中的Key不可以重复,value可以重复,(key只可以有一个null,value可以有多个value)

5)key和value是一对一的关系,即通过key一定可以找到value

10.2Map接口常用方法

Map map = new HashMap();
map.put("王宝强", "马蓉");
map.put("宋喆", "马蓉");
map.put("刘令博", null);
map.put(null, "刘亦菲");
map.put("鹿晗", "关晓彤");
map.put("lt","kqh");
map.put("lt","null");   //当添加的key值相同是,后的value会替换前面的value。
System.out.println(map);
//remove根据键删除关联的值
map.remove(null);
System.out.println(map);
//get 根据key获取value的值
System.out.println("根据key获取value的值"+map.get("宋喆"));
//size 获取集合中元素的数量
System.out.println("获取集合中元素的数量"+map.size());
//isEmpty 判断元素个数是否为0
System.out.println("判断元素个数是否为0"+map.isEmpty());
//containsKey判断键是否存在
System.out.println(" "+map.containsKey("lt"));
//clear 清空集合
map.clear();
System.out.println(map);

10.3Map接口的六大遍历方法

Node、Entry、EntrySet三者之间的关系。

Node结点是实际存储key-value的对象,Entry是Map的一个静态内部接口,里面有getkey,getvalue等方法。

entrySet()是一个返回key-value集合的方法。(在有大量元素的情况下,使用entrySet()遍历,效率会更高)

 Map map = new HashMap();
        map.put("邓超", "孙俪");
        map.put("王宝强", "马蓉");
        map.put("宋喆", "马蓉");
        map.put("刘令博", null);
        map.put(null, "刘亦菲");
        map.put("鹿晗", "关晓彤");
        //第一组  先取出key集合,然后再用增强for和迭代器遍历
        Set set = map.keySet();
        //(1)增强for
        for (Object key : set) {
            System.out.print(" "+map.get(key));
        }
        System.out.println();
        //(2)迭代器
        Iterator iterator = set.iterator();
        while (iterator.hasNext()) {
            Object key =  iterator.next();
            System.out.print(" "+map.get(key));
        }
        System.out.println();
        //第二组 直接把values取出
//        (3)
        Collection  collection  = map.values();
        for (Object values : collection) {
            System.out.print(" "+values);

        }
        System.out.println();
//        (4)
        Iterator iter = collection.iterator();
        while (iter.hasNext()) {
            Object values1 = iter.next();
            System.out.print(" "+values1);
        }
        System.out.println();
//        第三组直接在
//        (5)
        Set entrySet = map.entrySet();
        for (Object o : entrySet) {
            Map.Entry key = (Map.Entry) o;
            System.out.print(" " + key.getValue());
        }
        System.out.println();
//        (6)
        Iterator iter1 = entrySet.iterator();
        while (iter1.hasNext()) {
            Object next =  iter1.next();
            Map.Entry key = (Map.Entry) next;
            System.out.print(" "+key.getValue());
        }
        System.out.println();

        System.out.println();

11.1HashMap说明

1)HashMap是Map接口使用频率最高的实现类;

2)HashMap是Map的实现类所以是以key-value对的方法存储数据,key不可以重复,value可以重复;

3)底层是以hash表的方式存储数据,所以不保证数据映射的顺序;

4)HashMap没有实现同步,线程不安全.

HashMap的源码以及扩容机制等在前面的HashSet(本质是HashMap)中,讲过了(大致上是一样的,可以自己看看,研究一下)。

11.2Hashtable说明

1)Hashtable的key和value都不可以为null,否则会报NullPointerException

2)Hashtable的基本使用与HashMap一样。

3)Hashtable是线程安全级的(synchronized)。

所以当value和key为null是会报空指针异常

11.3Properties说明

1)Properties类继承Hashtable类并且实现了Map接口。

2)Properties通常用于从xxx.properties文件中,加载数据到Properties对象中,并进行读取和修改。

3)xxx.properties文件通常作为配置文件。

12.1TreeSet

public static void main(String[] args) {
//1. 当我们使用无参构造器,创建TreeSet 时,仍然是无序的
//2.如果希望添加的元素,按照字符串大小来排序
//3. 使用TreeSet 提供的一个构造器,可以传入一个比较器(匿名内部类)
// 并指定排序规则
/*
1. 构造器把传入的比较器对象,赋给了TreeSet 的底层的TreeMap 的属性this.comparator
public TreeMap(Comparator<? super K> comparator) {
    this.comparator = comparator;
}
2. 在调用treeSet.add("tom"), 在底层会执行到
if (cpr != null) {//cpr 就是我们的匿名内部类(对象)
do {
parent = t;
//动态绑定到我们的匿名内部类(对象)compare
cmp = cpr.compare(key, t.key);
if (cmp < 0)
    t = t.left;
else if (cmp > 0)
    t = t.right;
else //如果相等,即返回0,这个Key 就没有加入
    return t.setValue(value);
} while (t != null);
}
*/
    TreeSet treeSet = new TreeSet(new Comparator() {
        @Override
        public int compare(Object o1, Object o2) {
            //匿名内部类中,返回o1-o2 即按照从小到大顺序输出。
            return ((String) o1).length() - ((String) o2).length();
        }
    });
    treeSet.add("jack");
    treeSet.add("tom");//3
    treeSet.add("lt");
    treeSet.add("a");
    treeSet.add("abc");//3 在从写的内部类中,比较的是字符串的长度,所以字符串长度相等的字符串不能再次添加。
    System.out.println(treeSet);
}

13.1开发中如何选择集合实现类

在开发中,选择哪一个集合实现类,主要取决于业务的操作特点,然后根据集合实现类特性进行选择:

1.先判断存储的类型(一组对象[单列]或一组键值对[双列]

2.一组对象[单列]:Collection接口

允许重复:List接口

增删多:LinkedList

改查多:ArrayList

不允许重复:Set接口

无序:HashSet(数组+链表+红黑树)

排序:TreeSet

插入和取出顺序一致:LinkedHashSet

3.一组键值对[双列]:Map

键无序:HashMap

键排序:TreeMap

键插入和取出顺序一致:LinkedHashMao

读取文件:Properties

14.1Collections工具类说明

1)Collection是一个操作Set、List和Map等集合的工具类

2)Collection中提供了一系列的静态方法(可以直接用类调用)对集合进行排序、查询和修改等操作。

package com.lt;

import com.sun.xml.internal.ws.policy.privateutil.PolicyUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

@SuppressWarnings({"all"})
public class Collections_ {
    public static void main(String[] args) {
        ArrayList<String> slist = new ArrayList<>();
        slist.add("lutao1");
        slist.add("lutao2");
        slist.add("lutao3");
        slist.add("lutao4");
        slist.add("lutao5");
        slist.add("lutao");

        /*排序*/
//        Collections.reverse(slist);
//        System.out.println("反转"+slist);

//        Collections.shuffle(slist);
//        System.out.println("随机"+slist);

//        Collections.sort(slist);
//        System.out.println("按照自然顺序"+slist);

        //按照指定顺序顺序
//        Collections.sort(slist, new Comparator<String>() {
//            @Override
//            public int compare(String o1, String o2) {
//                return  o2.compareTo(o1);
//            }
//        });
//        System.out.println("按照指定顺序"+slist);

//        Collections.swap(slist,0,1);
//        System.out.println("交换后"+slist);

        /*查找、替换*/
//        System.out.println("返回集合中最大的元素"+Collections.max(slist));
//        System.out.println("按照指定顺序返回最大值"+Collections.max(slist, new Comparator<String>() {
//            @Override
//            public int compare(String o1, String o2) {
//                return o2.compareTo(o1);
//            }
//        }));

//        System.out.println("返回集合中最小值"+Collections.min(slist));
//        System.out.println("按照指定顺序返回最小值"+Collections.min(slist, new Comparator<String>() {
//            @Override
//            public int compare(String o1, String o2) {
//                return o1.compareTo(o2);
//            }
//        }));

//        System.out.println("返回集合中指定元素出现的次数"+Collections.frequency(slist,"lutao"));

//        ArrayList list = new ArrayList<>(8);
//        for (int i = 0; i < slist.size()+1; i++) {
//            list.add(null);
//        }
//        //再底层中,要求copy的集合长度大于被复制的集合
//        Collections.copy(list,slist);
//        System.out.println(list);

//        Collections.replaceAll(slist,"lutao","shuaige");
//        System.out.println("使用新值替换集合中的所以旧值"+slist);

    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值