【Java】四万字详写集合

集合关系图

集合类族总图

在这里插入图片描述

Collection类族

在这里插入图片描述

Map类族

在这里插入图片描述

集合概述

Q:什么是集合?
A:集合可以理解为一个容器,可以容纳各种数据类型;数组可以看做一个集合,它就是一个可以容纳多个一种数据类型的集合。

Q:集合在开发中有什么作用?
A:集合是一个容器、载体,可以容纳多个对象。在实际开发中,例如连接数据库,此时需要取出10条记录,java程序会自动将这10条记录封装成10个对象,再将这10个对象放到某一个集合中,将集合传到前端,然后遍历集合,将一个个数据全部展现出来

Q:集合中存储的是什么?
A:集合不能直接存储基本数据类型,集合也不能直接存储java对象,集合中存储的都是java对象的地址,存储基本数据类型时都会自动装箱(使用包装类对象)

注意:java中的集合有很多种,不同的集合其底层会有不同的数据结构,向不同的集合中存储数据,相当于向不同的数据结构中存储数据。

Q:什么是数据结构?
A:数据的存储结构就是数据结构。不同的数据结构,数据的存储方式不同。如:数组、二叉树、链表、哈希表等。

Q:集合在JDK的哪个包下?
A:java.util.*; 所有的集合类和集合接口都在java.util包

(重点)java中集合分为两大类:
一类是以存储数据方式存储元素:这一类集合中超级父接口:java.util.Collection;
一类是以键值对的方式存储元素:以键值对的方式存储元素,这一类集合中超级父接口:java.util.Map;

图解集合的存储

在这里插入图片描述

Iterable接口

此接口是Collection接口的父接口,所有元素都是可迭代的,可遍历的。

该接口最重要的是提供了1个遍历集合的方法:
Iterator< T > iterator() 返回类型为 T元素的迭代器对象。

在这里插入图片描述

Iterable接口源码

public interface Iterable<T> {

    Iterator<T> iterator();

    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

    default Spliterator<T> spliterator() {
        return Spliterators.spliteratorUnknownSize(iterator(), 0);
    }
}

Iterator接口

Iterator(迭代器)对象是专门用来遍历/迭代集合中元素的

该接口中的需要掌握方法有3个:
1.boolean hasNext() 如果迭代具有更多元素,则返回 true
2.E next() 返回迭代中的下一个元素(E代表元素类型)
3.void remove() 从底层集合中删除此迭代器返回的最后一个元素

在这里插入图片描述

Iterator接口源码

public interface Iterator<E> {
    boolean hasNext();
    
    E next();
    
    default void remove() {
        throw new UnsupportedOperationException("remove");
    }
    
    default void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }
}

Collection接口

Collection接口从Iterable接口继承而来(Is-a关系),所以Iterable接口有的方法Collection接口都继承了过来
Collection接口与Iterator接口是关联关系(Has-a关系),所以Iterator接口有的方法Collection接口都有

1.Collection接口源码

public interface Collection<E> extends Iterable<E> {
    boolean isEmpty();
    boolean contains(Object o);
    boolean add(E e);
    boolean remove(Object o);
    boolean containsAll(Collection<?> c);
    boolean addAll(Collection<? extends E> c);
    boolean removeAll(Collection<?> c);
    boolean retainAll(Collection<?> c);
    boolean equals(Object o);
    void clear();
    int size();
    int hashCode();
    Iterator<E> iterator();
    Object[] toArray();
    <T> T[] toArray(T[] a);

    default boolean removeIf(Predicate<? super E> filter) {
        Objects.requireNonNull(filter);
        boolean removed = false;
        final Iterator<E> each = iterator();
        while (each.hasNext()) {
            if (filter.test(each.next())) {
                each.remove();
                removed = true;
            }
        }
        return removed;
    }

    @Override
    default Spliterator<E> spliterator() {
        return Spliterators.spliterator(this, 0);
    }

    default Stream<E> stream() {
        return StreamSupport.stream(spliterator(), false);
    }

    default Stream<E> parallelStream() {
        return StreamSupport.stream(spliterator(), true);
    }
}

2.Collection方法

1.重要方法

继承的方法(掌握)
1.Iterator< T > iterator()返回类型为 T元素的迭代器对象
2.boolean hasNext() 如果迭代具有更多元素,则返回 true
3.E next() 返回迭代中的下一个元素(E代表元素类型)
4.void remove() 从底层集合中删除此迭代器指向的元素

需要掌握的特有的方法:
1.boolean add(Object e) 向集合中添加元素
2.int size() 返回此集合元素数
3.void clear() 删除此集合所有元素
4.boolean remove(Object o) 删除指定一个元素
5.boolean contains(Object o) 判断集合是否包含某元素
6.boolean isEmpty() 判断集合元素是否为空
7.Object[] toArray() 返回一个包含此集合所有元素的数组

测试特有方法

//创建一个集合对象
Collection c = new ArrayList();		

//测试add方法
c.add(100);			//添加一个整型数据
c.add(3.14);		//添加一个double或者float型数据
c.add(true);		//添加一个boolean型数据
c.add('h');			//添加一个字符型数据
c.add("hello");		//添加一个引用数据类型型数据
c.add(new Dog());	//添加一个Dog类对象

//测试size方法
int Size = c.size();		//返回集合中元素的个数
System.out.println(Size);	//输出:6

//测试isEmpty方法
boolean Flag = c.isEmpty();	//返回值:true(集合为空) false(集合不为空)
System.out.println(Flag);	//输出:false

//测试remove方法
c.remove(100);		//删除100这个对象,集合中只剩下五个元素
System.out.println(c.size());	//输出:5

//测试contains方法
boolean isContain = c.contains(3.14);	//判断是否包含3.14这个对象
System.out.println(isContain );		//输出:true

//测试toArray方法
Object[] objects = c.toArray();		//将集合中的元素装入一个Object类对象数组中
for(int i = 0; i < objects.length;i++){
    System.out.print(objects[i] + " ");	
    //输出:true h hello 3.14 Collection.Dog@4554617c
}

//测试clear方法
c.clear();				//清空集合,删除所有元素
System.out.println(c.isEmpty());	//输出:true

2.遍历集合方法

法一:使用迭代器遍历集合
        //创建一个集合对象
        Collection c = new ArrayList();

        //向对象中添加元素
        c.add(1);
        c.add(2);
        c.add(3);
        c.add(4);

        //测试iterator方法,获取迭代器对象
        Iterator it = c.iterator();

        while(it.hasNext()){    //hasNext方法:如果集合没有遍历结束则返回true
            Object obj = it.next(); //next方法:获取集合中的对象
            System.out.println(obj);    //将对象输出
            //输出:1 2 3 4
        }

        it.remove();
        //删除迭代器此时指向的对象(由于之前遍历了集合,所以此时迭代器指向最后一个输出的那个对象)
        
        Iterator it1 = c.iterator();	//重新获取迭代器

        while(it1.hasNext()){    //hasNext方法:如果集合没有遍历结束则返回true
            Object obj = it1.next(); //next方法:获取集合中的对象
            System.out.println(obj);    //将对象输出
            //输出:1 2 3
        }

迭代器遍历原理图解
在这里插入图片描述

迭代器说明:集合对象调用iterator()方法时,那一瞬间,iterator对象就向一张照片一样将集合中的数据进行了“拍照”,然后根据这张“照片”进行遍历,如果在遍历过程中对原集合进行数据的增删(或者说在迭代过程中使用集合对象的remove方法和add方法),会出现异常:java.util.ConcurrentModificationException,当集合结构改变时,应该重新获取新的迭代器,如果想在遍历时,同步删除集合元素,应该使用iterator对象的remove方法。
见下面程序,会对迭代器有更深的理解

        Collection c = new ArrayList();
        Iterator it;

        c.add("hello");
        c.add("world");
        c.add("kitty");

        it = c.iterator();

        while(it.hasNext()){
            Object o = it.next();
            it.remove();
            System.out.println("此时集合中还有"+c.size()+"个数据");
            System.out.println("删除的数据为:" + o);
        }

程序运行结果
在这里插入图片描述

法二:使用foreach遍历集合

JDK5.0新特性:增强版本for循环
*语法:
for(元素类型 变量名:数组或集合)
{
System.out.println(变量名);
}

测试代码

    public static void main(String[] args) {
        int[] array = {51,5,15};
        //增强for遍历数组
        for(int i: array){
            System.out.println(i);
        }
        
        System.out.println("-----------------------");
        
        //增强for遍历集合
        List<String> myList = new LinkedList<>();
        myList.add("abc");
        myList.add("def");
        for (String s:myList) {
            System.out.println(s);
        }
    }

程序输出
在这里插入图片描述

3.remove方法和contains方法详解(含源码)

boolean remove(Object o) 删除指定一个元素

源码注释
在这里插入图片描述

boolean contains(Object o) 判断集合是否包含某元素

源码注释
在这里插入图片描述

由源码注释可以看出,这两个方法底层都是调用了equals方法进行比对确定目标元素进行相应操作

Object类中remove方法:比较的是两个方法的Hash值,而Hash值是根据对象地址计算出来的,所以相当于比较的是地址。

    public boolean equals(Object obj) {
        return (this == obj);
    }

因此,remove方法和contains方法都是调用了equals方法,将形参地址与集合中的所有对象地址进行一个一个对比,地址相同则视为同一元素,再进行相应的操作。
当我们传对象时,你是想要比较的是地址还是对象内容,这取决于你是否重写equals方法

比较字符串,由于String类中重写了equals方法,所以比较的是内容而不是地址

 Collection c = new ArrayList();

 String s1 = new String("xyz");
 String s2 = new String("xyz");
 c.add(s1);
 System.out.println(c.contains(s2));	//true

比较自定义User类,由于没有重写equals方法,所以比较的是地址

Collection c = new ArrayList();

 User user1 = new User("jack");
 User user2 = new User("jack");
 c.add(user1);
 System.out.println(c.contains(user2));	//false
 
class User{
    private String name;

    public User() {
    }

    public User(String name) {
        this.name = name;
    }
}

比较自定义User类,由于重写了equals方法,所以比较的是内容而不是地址

Collection c = new ArrayList();

 User user1 = new User("jack");
 User user2 = new User("jack");
 c.add(user1);
 System.out.println(c.contains(user2));	//true


class User{
    private String name;

    public User() {
    }

    public User(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object obj) {
        if(obj == null || !(obj instanceof User)) {
            return false;
        }
        if(obj == this) {
            return true;
        }
        User u = (User)obj;
        return u.name.equals(this.name);
    }

}

List接口

List接口从Collection接口继承而来,实现该接口的集合类有:ArrayList、LinkedList、Vector

List集合存储元素特点:①有序 ②可重复 ③元素有下标
有序:遍历取出元素的顺序和放入元素的顺序一样,并不是说按照大小排序
可重复:可以放进去两个一模一样的元素,两个都会在集合中存在
有下标:下标从0开始,以1递增

接口源码

public interface List<E> extends Collection<E> {
    int size();
    boolean isEmpty();
    boolean contains(Object o);
    Iterator<E> iterator();
    Object[] toArray();
    <T> T[] toArray(T[] a);
    boolean add(E e);
    boolean remove(Object o);
    boolean containsAll(Collection<?> c);
    boolean addAll(Collection<? extends E> c);
    boolean addAll(int index, Collection<? extends E> c);
    boolean removeAll(Collection<?> c);
    boolean retainAll(Collection<?> c);
    void clear();
    boolean equals(Object o);
    int hashCode();
    E get(int index);
    E set(int index, E element);
    void add(int index, E element);
    E remove(int index);
    int indexOf(Object o);
    int lastIndexOf(Object o);
    ListIterator<E> listIterator();
    ListIterator<E> listIterator(int index);
    List<E> subList(int fromIndex, int toIndex);

    default void replaceAll(UnaryOperator<E> operator) {
        Objects.requireNonNull(operator);
        final ListIterator<E> li = this.listIterator();
        while (li.hasNext()) {
            li.set(operator.apply(li.next()));
        }
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    default void sort(Comparator<? super E> c) {
        Object[] a = this.toArray();
        Arrays.sort(a, (Comparator) c);
        ListIterator<E> i = this.listIterator();
        for (Object e : a) {
            i.next();
            i.set((E) e);
        }
    }

    @Override
    default Spliterator<E> spliterator() {
        return Spliterators.spliterator(this, Spliterator.ORDERED);
    }
}

List接口需要掌握的方法:
1.void add(int index, Object element)将指定的元素插入此列表中的指定位置,其他元素后移,由于后移操作导致效率较低
2.Object get(int index) 取出集合中指定下标的元素
3.int indexOf(Object o) 返回指定元素第一次出现的下标
4.int lastIndexOf(Object o) 返回指定元素最后一次出现的下标
5.Object remove(int index) 将指定下标的元素进行删除并返回
6.Object set(int index, Object element) 将指定位置的元素返回并替换成指定元素

方法测试

//创建List集合
List myList = new ArrayList();
//添加元素
myList.add("A");
myList.add("B");
myList.add("C");
myList.add("D");	//集合中有 A B C D四个元素,下标分别为0 1 2 3

myList.add(4,"A");	//添加一个下标为4的 A,此时集合有五个元素

//迭代
Iterator it = myList.iterator();
while(it.hasNext())
{
	Object obj = it.next();
	System.out.println(obj);	//输出:A B C D A
}

Object obj = myList.get(1);	//将下标为1的元素取出来
System.out.println(obj);	//输出:B

int index = myList.indexOf("E");	//将元素E第一次出现的下标取出来
System.out.println(index);	//输出:-1(原因:不存在E元素)

int lastIndex = myList.lastIndexOf("A");	//取出最后一个A的下标
System.out.println(lastIndex);		//输出:4

 Object obj = myList.remove(4);	//从集合中删除下标为4的元素,将该元素放入obj中
 System.out.println(obj);		//输出:A

Object obj = myList.set(3,"F");	//将下标为3的元素更换为F,并返回原元素
System.out.println(obj);	//输出:D

ArrayList类

1.特点

ArrayList类实现了List接口

ArrayList类的特点:
1.ArrayList集合底层是一个Object数组
2.默认初始化容量为10(刚创建出来时容量为0,添加一个元素后默认初始为10)
3.当ArrayList集合满时,自动扩容为原来的1.5倍
4.ArrayList集合是非线程安全的,其方法没有用synchronized修饰
5.由于底层是数组,所以检索效率高随机增删效率低,存储大数据量不方便,该集合适用于检索操作多、随机增删操作少的情形

2.源码
底层数据结构

在这里插入图片描述

默认容量

在这里插入图片描述

默认初始化容量为10

自动扩容

在这里插入图片描述

int newCapacity = oldCapacity + (oldCapacity >> 1);
">>"是右移操作符,移动一位就是原来的0.5倍
这行代码意思是:新容量 = 旧容量 + 0.5 * 旧容量

构造方法

构造方法有三种:
new ArrayList(); 使用默认初始化容量
new ArrayList(int Capacity); 自己规定默认初始化容量
new ArrayList(Collection<? extends E> c); 传入一个集合初始化

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.ArrayList的使用
    public static void main(String[] args) {
        //默认初始化容量10
        List myList1 = new ArrayList();
        //指定初始化容量100
        List myList2 = new ArrayList(100);
        //创建一个HashSet集合
        Collection c = new HashSet();
        //添加元素进入HashSet集合
        c.add(100);
        c.add(200);
        c.add(900);
        c.add(50);
        //利用构造方法将HashSet集合转换成List集合
        List myList3 = new ArrayList(c);
        //遍历输出myList3
        for (int i = 0; i < myList3.size(); i++) {
            System.out.println(myList3.get(i));
        }
    }

LinkedList类

1.特点

LinkedList类实现了List接口

LinkedList类的特点:
1.LinkedList底层是双向链表数据结构
2.存入的数据是有下标
3.优点:随机增删效率高
4.缺点:查找效率低(原因:每次查找都是从头开始查找,复杂度为O(n))
5.链表上的元素在内存中的地址不连续

2.双向链表

双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。

双向链表图解
在这里插入图片描述

3.LinkedList内存图解

在这里插入图片描述

4.源码

底层数据结构双向链表源码

在这里插入图片描述

头结点和尾节点源码

在这里插入图片描述

5.LinkedList的使用
    public static void main(String[] args) {
        LinkedList list = new LinkedList();

        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        //此时集合中的数据:1 2 3 4

        list.removeFirst();	//删除链表最后一个元素
        list.removeLast();	//删除链表第一个元素
        //此时集合中的数据:2 3 

        list.addFirst(0);	//向链表头插入数据
        list.addLast(5);	//向链表尾插入数据
        //此时集合中的数据:0 2 3 5
        
    }

Vector类

1.特点

Vector类实现了List接口

Vector类的特点:
1.底层是一个Object数组
2.初始化容量为10
3.集合满了之后,自动扩容为原来的2倍
4.是线程安全的,所有方法都是用synchronized修饰
5.效率较低

2.源码

底层数据结构源码

在这里插入图片描述

初始化容量源码

在这里插入图片描述

自动扩容源码

在这里插入图片描述

3.Vector的使用
 public static void main(String[] args) {
 	//创建Vector对象
     Vector v = new Vector();
     
     //向集合中添加元素
     v.add(1);
     v.add("111");
     v.add("222");
     v.add("333");
     
	//遍历输出集合中元素
     for (Object o:v) {
         System.out.println(o);
     }
 }
4.将ArrayList类对象变成Vector类对象

ArrayList类和Vector类非常相似,前者是线程不安全的,后者是线程安全的,那么如何将线程不安全的ArrayList类对象变成线程安全的Vector类对象呢?
A:使用Collections工具类

区分Collection和Collections
java.util.Collection; ---->集合接口(是一个接口)
java.util.Collections; ----->集合工具类(是一个类)

转换测试代码

    public static void main(String[] args) {
        //非线程安全
        List myList = new ArrayList();
        
        //转线程安全
        Collections.synchronizedList(myList);
        
        //myList集合就是线程安全的了
        myList.add("111");
        myList.add("222");
        myList.add("333");

        for (Object o:myList) {
            System.out.println(o);
        }

    }

Set接口

Set接口从Collection接口继承而来
实现该接口的集合类有:HashSet接口
继承该接口的接口有:AbstractSet接口
实现了AbstractSet接口的类有:TreeSet类

Set接口特点:①不可重复 ②存取无顺序 ③无下标
存取无顺序:意思是取出元素的顺序不一定是存进元素的顺序
不可重复:意思是集合中不会有两个一模一样的对象,重复的元素会覆盖之前的元素

Set接口源码

public interface Set<E> extends Collection<E> {
    int size();
    boolean isEmpty();
    boolean contains(Object o);
    Iterator<E> iterator();
    Object[] toArray();
    <T> T[] toArray(T[] a);
    boolean add(E e);
    boolean remove(Object o);
    boolean containsAll(Collection<?> c);
    boolean addAll(Collection<? extends E> c);
    boolean retainAll(Collection<?> c);
    boolean removeAll(Collection<?> c);
    void clear();
    boolean equals(Object o);
    int hashCode();

    @Override
    default Spliterator<E> spliterator() {
        return Spliterators.spliterator(this, Spliterator.DISTINCT);
    }
}

HashSet类

1.特点

HashSet类实现了Set接口

HashSet类特点:
1.存取无序且不可重复
2.HashSet底层调用的是HashMap,将数据放入HashMap的Key部分,其value默认是一个Object类对象
3.HashSet初始化容量为16,加载因子为0.75。集合容量达到75%之后,自动扩容为原来的两倍

2.源码

HashSet类源码

public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable
{
    static final long serialVersionUID = -5024744406713321676L;

    private transient HashMap<E,Object> map;

    // Dummy value to associate with an Object in the backing Map
    private static final Object PRESENT = new Object();

    public HashSet() {
        map = new HashMap<>();
    }
    public HashSet(Collection<? extends E> c) {
        map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
        addAll(c);
    }
    public HashSet(int initialCapacity, float loadFactor) {
        map = new HashMap<>(initialCapacity, loadFactor);
    }
    public HashSet(int initialCapacity) {
        map = new HashMap<>(initialCapacity);
    }
    HashSet(int initialCapacity, float loadFactor, boolean dummy) {
        map = new LinkedHashMap<>(initialCapacity, loadFactor);
    }
    public Iterator<E> iterator() {
        return map.keySet().iterator();
    }
    public int size() {
        return map.size();
    }
    public boolean isEmpty() {
        return map.isEmpty();
    }
    public boolean contains(Object o) {
        return map.containsKey(o);
    }
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }
    public boolean remove(Object o) {
        return map.remove(o)==PRESENT;
    }
    public void clear() {
        map.clear();
    }

    @SuppressWarnings("unchecked")
    public Object clone() {
        try {
            HashSet<E> newSet = (HashSet<E>) super.clone();
            newSet.map = (HashMap<E, Object>) map.clone();
            return newSet;
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e);
        }
    }
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        // Write out any hidden serialization magic
        s.defaultWriteObject();

        // Write out HashMap capacity and load factor
        s.writeInt(map.capacity());
        s.writeFloat(map.loadFactor());

        // Write out size
        s.writeInt(map.size());

        // Write out all elements in the proper order.
        for (E e : map.keySet())
            s.writeObject(e);
    }
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // Read in any hidden serialization magic
        s.defaultReadObject();

        // Read capacity and verify non-negative.
        int capacity = s.readInt();
        if (capacity < 0) {
            throw new InvalidObjectException("Illegal capacity: " +
                                             capacity);
        }

        // Read load factor and verify positive and non NaN.
        float loadFactor = s.readFloat();
        if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
            throw new InvalidObjectException("Illegal load factor: " +
                                             loadFactor);
        }

        // Read size and verify non-negative.
        int size = s.readInt();
        if (size < 0) {
            throw new InvalidObjectException("Illegal size: " +
                                             size);
        }

        // Set the capacity according to the size and load factor ensuring that
        // the HashMap is at least 25% full but clamping to maximum capacity.
        capacity = (int) Math.min(size * Math.min(1 / loadFactor, 4.0f),
                HashMap.MAXIMUM_CAPACITY);

        // Create backing HashMap
        map = (((HashSet<?>)this) instanceof LinkedHashSet ?
               new LinkedHashMap<E,Object>(capacity, loadFactor) :
               new HashMap<E,Object>(capacity, loadFactor));

        // Read in all elements in the proper order.
        for (int i=0; i<size; i++) {
            @SuppressWarnings("unchecked")
                E e = (E) s.readObject();
            map.put(e, PRESENT);
        }
    }
    public Spliterator<E> spliterator() {
        return new HashMap.KeySpliterator<E,Object>(map, 0, -1, 0, 0);
    }
}

底层源码解析
    private transient HashMap<E,Object> map;
        /**
     * Constructs a new, empty set; the backing <tt>HashMap</tt> instance has
     * default initial capacity (16) and load factor (0.75).
     */
    public HashSet() {
        map = new HashMap<>();
    }

由这部分源码可以看出,HashSet底层是一个HashMap对象map组成,HashSet默认构造函数就是为map分配一个堆空间
默认初始容量为16,默认加载因子为0.75

add方法源码解析
    private static final Object PRESENT = new Object();
    public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

从add()方法源码中可以看出,加入的元素数据放入的是HashMap的key部分,而Value部分默认是一个Object类对象,这个对象叫PRESENT。map对象调用了它的put(K key,V value)方法,将数据e和对象PRESENT放入map中

3.HashSet的使用

测试代码

 public static void main(String[] args) {
     HashSet<String> mySet = new HashSet<>();

     mySet.add("hello1");
     mySet.add("hello2");
     mySet.add("hello3");
     mySet.add("hello4");
     mySet.add("hello5");
     mySet.add("hello5");
     mySet.add("hello5");
     mySet.add("hello5");
     mySet.add("hello5");

     for (String s:mySet) {
         System.out.println(s);
     }
 }

程序运行结果
在这里插入图片描述

由上述程序运行结果可以看出:HashSet集合无序不可重复。

AbstractSet接口

AbstractSet接口实现了Set接口,TreeSet类实现了AbstractSet接口

AbstractSet接口源码

public abstract class AbstractSet<E> extends AbstractCollection<E> implements Set<E> {
    protected AbstractSet() {}

    public boolean equals(Object o) {
        if (o == this)
            return true;
        if (!(o instanceof Set))
            return false;
        Collection<?> c = (Collection<?>) o;
        if (c.size() != size())
            return false;
        try {
            return containsAll(c);
        } catch (ClassCastException unused)   {
            return false;
        } catch (NullPointerException unused) {
            return false;
        }
    }

    public int hashCode() {
        int h = 0;
        Iterator<E> i = iterator();
        while (i.hasNext()) {
            E obj = i.next();
            if (obj != null)
                h += obj.hashCode();
        }
        return h;
    }

    public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        boolean modified = false;

        if (size() > c.size()) {
            for (Iterator<?> i = c.iterator(); i.hasNext(); )
                modified |= remove(i.next());
        } else {
            for (Iterator<?> i = iterator(); i.hasNext(); ) {
                if (c.contains(i.next())) {
                    i.remove();
                    modified = true;
                }
            }
        }
        return modified;
    }
}
TreeSet类
1.特点

TreeSet类实现了AbstractSet接口

TreeSet类的特点:
①存取无顺序:取出数据的顺序可能和存入数据的顺序不一样
②不可重复 :重复的数据会覆盖原数据
③遍历输出默认自动排序为升序输出
④TreeSet类底层是TreeMap对象,TreeMap也是ket-value型
⑤由于TreeMap底层是二叉树数据结构,所以TreeSet底层就是二叉树数据结构(在TreeMap中详细叙述)

2.源码

TreeSet类源码

public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, java.io.Serializable
{
    private transient NavigableMap<E,Object> m;
    private static final Object PRESENT = new Object();
    TreeSet(NavigableMap<E,Object> m) {
        this.m = m;
    }
    public TreeSet() {
        this(new TreeMap<E,Object>());
    }
    public TreeSet(Comparator<? super E> comparator) {
        this(new TreeMap<>(comparator));
    }
    public TreeSet(Collection<? extends E> c) {
        this();
        addAll(c);
    }
    public TreeSet(SortedSet<E> s) {
        this(s.comparator());
        addAll(s);
    }
    public Iterator<E> iterator() {
        return m.navigableKeySet().iterator();
    }
    public Iterator<E> descendingIterator() {
        return m.descendingKeySet().iterator();
    }
    public NavigableSet<E> descendingSet() {
        return new TreeSet<>(m.descendingMap());
    }
    public int size() {
        return m.size();
    }
    public boolean isEmpty() {
        return m.isEmpty();
    }
    public boolean contains(Object o) {
        return m.containsKey(o);
    }
    public boolean add(E e) {
        return m.put(e, PRESENT)==null;
    }
    public boolean remove(Object o) {
        return m.remove(o)==PRESENT;
    }
    public void clear() {
        m.clear();
    }
    public  boolean addAll(Collection<? extends E> c) {
        // Use linear-time version if applicable
        if (m.size()==0 && c.size() > 0 &&
            c instanceof SortedSet &&
            m instanceof TreeMap) {
            SortedSet<? extends E> set = (SortedSet<? extends E>) c;
            TreeMap<E,Object> map = (TreeMap<E, Object>) m;
            Comparator<?> cc = set.comparator();
            Comparator<? super E> mc = map.comparator();
            if (cc==mc || (cc != null && cc.equals(mc))) {
                map.addAllForTreeSet(set, PRESENT);
                return true;
            }
        }
        return super.addAll(c);
    }
    public NavigableSet<E> subSet(E fromElement, boolean fromInclusive,
                                  E toElement,   boolean toInclusive) {
        return new TreeSet<>(m.subMap(fromElement, fromInclusive,
                                       toElement,   toInclusive));
    }
    public NavigableSet<E> headSet(E toElement, boolean inclusive) {
        return new TreeSet<>(m.headMap(toElement, inclusive));
    }
    public NavigableSet<E> tailSet(E fromElement, boolean inclusive) {
        return new TreeSet<>(m.tailMap(fromElement, inclusive));
    }
    public SortedSet<E> subSet(E fromElement, E toElement) {
        return subSet(fromElement, true, toElement, false);
    }
    public SortedSet<E> headSet(E toElement) {
        return headSet(toElement, false);
    }
    public SortedSet<E> tailSet(E fromElement) {
        return tailSet(fromElement, true);
    }
    public Comparator<? super E> comparator() {
        return m.comparator();
    }
    public E first() {
        return m.firstKey();
    }
    public E last() {
        return m.lastKey();
    }
    public E lower(E e) {
        return m.lowerKey(e);
    }
    public E floor(E e) {
        return m.floorKey(e);
    }
    public E ceiling(E e) {
        return m.ceilingKey(e);
    }
    public E higher(E e) {
        return m.higherKey(e);
    }
    public E pollFirst() {
        Map.Entry<E,?> e = m.pollFirstEntry();
        return (e == null) ? null : e.getKey();
    }
    public E pollLast() {
        Map.Entry<E,?> e = m.pollLastEntry();
        return (e == null) ? null : e.getKey();
    }
    @SuppressWarnings("unchecked")
    public Object clone() {
        TreeSet<E> clone;
        try {
            clone = (TreeSet<E>) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new InternalError(e);
        }

        clone.m = new TreeMap<>(m);
        return clone;
    }
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException {
        // Write out any hidden stuff
        s.defaultWriteObject();

        // Write out Comparator
        s.writeObject(m.comparator());

        // Write out size
        s.writeInt(m.size());

        // Write out all elements in the proper order.
        for (E e : m.keySet())
            s.writeObject(e);
    }
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        // Read in any hidden stuff
        s.defaultReadObject();

        // Read in Comparator
        @SuppressWarnings("unchecked")
            Comparator<? super E> c = (Comparator<? super E>) s.readObject();

        // Create backing TreeMap
        TreeMap<E,Object> tm = new TreeMap<>(c);
        m = tm;

        // Read in size
        int size = s.readInt();

        tm.readTreeSet(size, s, PRESENT);
    }
    public Spliterator<E> spliterator() {
        return TreeMap.keySpliteratorFor(m);
    }
    private static final long serialVersionUID = -2479143000061671589L;
}
底层数据结构源码解析

    public TreeSet() {
        this(new TreeMap<E,Object>());
    }

由此可见,TreeSet默认构造方法构造了一个TreeMap对象

add方法源码解析
	private static final Object PRESENT = new Object();
    public boolean add(E e) {
        return m.put(e, PRESENT)==null;
    }

从add()方法源码中可以看出,加入的元素数据放入的是TreeMap的key部分,而Value部分默认是一个Object类对象,这个对象叫PRESENT。map对象调用了它的put(K key,V value)方法,将数据e和对象PRESENT放入map中

3.TreeSet的使用

TreeSet测试程序

    public static void main(String[] args) {
    
        TreeSet<String> ts1 = new TreeSet<>();
        ts1.add("b");
        ts1.add("a");
        ts1.add("d");
        ts1.add("d");
        ts1.add("e");
        ts1.add("f");
        ts1.add("g");
        for (String s:ts1) {
            //按照字母升序排序
            System.out.println(s);
        }
        
        System.out.println("********************************");
        
        TreeSet<Integer> ts2 = new TreeSet<>();
        ts2.add(100);
        ts2.add(99);
        ts2.add(98);
        ts2.add(97);
        ts2.add(97);
        ts2.add(96);
        ts2.add(95);
        for (Integer it:ts2) {
            //按照数字升序排序
            System.out.println(it);
        }
    }

程序运行结果
在这里插入图片描述

Map接口

Map接口特点

Map接口特点:
1.Map接口和Collection接口没有任何关系
2.Map接口下的集合都是以KeyValue这种键值对的形式存储元素
3.Key和Value都是存储的Java对象的地址
4.所有Map借口下集合类中Key的特点:无序不可重复
5.实现该接口的集合类有:HashMap类、HashTable类
6.继承该接口的接口有:SortedMap接口

Map接口源码

public interface Map<K,V> {
    int size();
    boolean isEmpty();
    boolean containsKey(Object key);
    boolean containsValue(Object value);
    V get(Object key);
    V put(K key, V value);
    V remove(Object key);
    void putAll(Map<? extends K, ? extends V> m);
    void clear();
    Set<K> keySet();
    Collection<V> values();
    Set<Map.Entry<K, V>> entrySet();
    boolean equals(Object o);
    int hashCode();

    interface Entry<K,V> {
        K getKey();
        V getValue();
        V setValue(V value);
        boolean equals(Object o);
        int hashCode();

        public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() {
            return (Comparator<Map.Entry<K, V>> & Serializable)
                (c1, c2) -> c1.getKey().compareTo(c2.getKey());
        }
        public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K,V>> comparingByValue() {
            return (Comparator<Map.Entry<K, V>> & Serializable)
                (c1, c2) -> c1.getValue().compareTo(c2.getValue());
        }
        public static <K, V> Comparator<Map.Entry<K, V>> comparingByKey(Comparator<? super K> cmp) {
            Objects.requireNonNull(cmp);
            return (Comparator<Map.Entry<K, V>> & Serializable)
                (c1, c2) -> cmp.compare(c1.getKey(), c2.getKey());
        }
        public static <K, V> Comparator<Map.Entry<K, V>> comparingByValue(Comparator<? super V> cmp) {
            Objects.requireNonNull(cmp);
            return (Comparator<Map.Entry<K, V>> & Serializable)
                (c1, c2) -> cmp.compare(c1.getValue(), c2.getValue());
        }
    }

    default V getOrDefault(Object key, V defaultValue) {
        V v;
        return (((v = get(key)) != null) || containsKey(key))
            ? v
            : defaultValue;
    }
    default void forEach(BiConsumer<? super K, ? super V> action) {
        Objects.requireNonNull(action);
        for (Map.Entry<K, V> entry : entrySet()) {
            K k;
            V v;
            try {
                k = entry.getKey();
                v = entry.getValue();
            } catch(IllegalStateException ise) {
                // this usually means the entry is no longer in the map.
                throw new ConcurrentModificationException(ise);
            }
            action.accept(k, v);
        }
    }
    default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
        Objects.requireNonNull(function);
        for (Map.Entry<K, V> entry : entrySet()) {
            K k;
            V v;
            try {
                k = entry.getKey();
                v = entry.getValue();
            } catch(IllegalStateException ise) {
                // this usually means the entry is no longer in the map.
                throw new ConcurrentModificationException(ise);
            }

            // ise thrown from function is not a cme.
            v = function.apply(k, v);

            try {
                entry.setValue(v);
            } catch(IllegalStateException ise) {
                // this usually means the entry is no longer in the map.
                throw new ConcurrentModificationException(ise);
            }
        }
    }

    default V putIfAbsent(K key, V value) {
        V v = get(key);
        if (v == null) {
            v = put(key, value);
        }
        return v;
    }
    default boolean remove(Object key, Object value) {
        Object curValue = get(key);
        if (!Objects.equals(curValue, value) ||
            (curValue == null && !containsKey(key))) {
            return false;
        }
        remove(key);
        return true;
    }
    default boolean replace(K key, V oldValue, V newValue) {
        Object curValue = get(key);
        if (!Objects.equals(curValue, oldValue) ||
            (curValue == null && !containsKey(key))) {
            return false;
        }
        put(key, newValue);
        return true;
    }
    default V replace(K key, V value) {
        V curValue;
        if (((curValue = get(key)) != null) || containsKey(key)) {
            curValue = put(key, value);
        }
        return curValue;
    }
    default V computeIfAbsent(K key,
            Function<? super K, ? extends V> mappingFunction) {
        Objects.requireNonNull(mappingFunction);
        V v;
        if ((v = get(key)) == null) {
            V newValue;
            if ((newValue = mappingFunction.apply(key)) != null) {
                put(key, newValue);
                return newValue;
            }
        }
        return v;
    }
    default V computeIfPresent(K key,
            BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
        Objects.requireNonNull(remappingFunction);
        V oldValue;
        if ((oldValue = get(key)) != null) {
            V newValue = remappingFunction.apply(key, oldValue);
            if (newValue != null) {
                put(key, newValue);
                return newValue;
            } else {
                remove(key);
                return null;
            }
        } else {
            return null;
        }
    }
    default V compute(K key,
            BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
        Objects.requireNonNull(remappingFunction);
        V oldValue = get(key);

        V newValue = remappingFunction.apply(key, oldValue);
        if (newValue == null) {
            // delete mapping
            if (oldValue != null || containsKey(key)) {
                // something to remove
                remove(key);
                return null;
            } else {
                // nothing to do. Leave things as they were.
                return null;
            }
        } else {
            // add or replace old mapping
            put(key, newValue);
            return newValue;
        }
    }
    default V merge(K key, V value,
            BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
        Objects.requireNonNull(remappingFunction);
        Objects.requireNonNull(value);
        V oldValue = get(key);
        V newValue = (oldValue == null) ? value :
                   remappingFunction.apply(oldValue, value);
        if(newValue == null) {
            remove(key);
        } else {
            put(key, newValue);
        }
        return newValue;
    }
}

Map常用方法

  • V put(K key, V value) 向Map集合中添加键值对
  • V get(Object key) 通过key获取value
  • void clear() 清空Map集合
  • boolean containsKey(Object key) 判断Map中是否包含某个key
  • boolean containsValue(Object value) 判断Map中是否包含某个value
  • boolean isEmpty() 判断Map集合中元素个数是否为0
  • Set keySet() 获取Map中所有的key,返回一个set集合
  • V remove(Object key) 通过key删除键值对
  • int size() 获取Map集合中键值对的个数
  • Collection values() 获取Map集合中所有的value,返回一个Collection
  • Set<Map.Entry<K,V>> entrySet() 将Map集合转换成Set集合
  • Collection values() 获取Map集合中所有的value,返回一个Collection
  • Set keySet() 获取Map集合中所有的key(所有的键是一个set集合)

Map中常用方法测试

    public static void main(String[] args) {
        //创建一个Map类对象:Key部分是Integer类对象,Value部分是String类对象
        Map<Integer,String> map = new HashMap<>();

        //测试put方法:向集合中添加元素
        map.put(1,"zhangsan");
        map.put(2,"lisi");
        map.put(3,"wangwu");

        //测试Size方法:获取集合中有多少个元素
        int mapSize = map.size();   //3
        System.out.println(mapSize);

        //测试get方法:通过Key获取对应的Value
        String s1 = map.get(1);
        String s2 = map.get(2);
        String s3 = map.get(3);

        System.out.println("k=1--->V=" + s1);
        System.out.println("k=2--->V=" + s2);
        System.out.println("k=3--->V=" + s3);

        //测试containsKey方法:判断集合中是否包含某一个Key
        boolean containKey1 = map.containsKey(1);   //true
        boolean containKey2 = map.containsKey(10);  //false

        System.out.println(containKey1);
        System.out.println(containKey2);

        //测试containsValue方法:判断集合中是否包含某一个Value
        boolean containValue1 = map.containsValue("zhangsan");  //true
        boolean containValue2 = map.containsValue("zhaoliu");   //false

        System.out.println(containValue1);
        System.out.println(containValue2);

        //测试isEmpty方法:判断集合中是否为空
        boolean empty = map.isEmpty();  //false

        System.out.println(empty);

        //测试remove方法:通过Key来删除整个这个键值对,并返回其Value值
        String removeValue = map.remove(1); //zhangsan

        System.out.println(removeValue);

        //测试keySet方法:获取Map对象中所有的key,这些key放在一个Set中
        Set<Integer> set = map.keySet();

        for (Integer i:set) {
            System.out.println(i);  //2 3
        }

        //测试Values方法:获取map对象中所有的Value,这些Value放在一个Collection中
        Collection<String> c = map.values();

        for (String s: c) {
            System.out.println(s);  //lisi wangwu
        }

        //测试entrySet方法:将Map集合转换为Set集合(key和Value合并)
        Set< Map.Entry<Integer,String> > set1 = map.entrySet();

        for (Map.Entry<Integer,String> e:set1) {
            System.out.println(e);  //2=lisi 3=wangwu
        }

        //测试clear方法:将Map集合中的元素全部删除
        map.clear();

        int mapClear = map.size();
        System.out.println(mapClear);   //0
        
    }

程序运行结果
在这里插入图片描述

entrySet方法详解

Map接口内部Entry接口源码

    interface Entry<K,V> {
        K getKey();
        V getValue();
        V setValue(V value);
        boolean equals(Object o);
        int hashCode();

        public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() {
            return (Comparator<Map.Entry<K, V>> & Serializable)
                (c1, c2) -> c1.getKey().compareTo(c2.getKey());
        }
        public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K,V>> comparingByValue() {
            return (Comparator<Map.Entry<K, V>> & Serializable)
                (c1, c2) -> c1.getValue().compareTo(c2.getValue());
        }
        public static <K, V> Comparator<Map.Entry<K, V>> comparingByKey(Comparator<? super K> cmp) {
            Objects.requireNonNull(cmp);
            return (Comparator<Map.Entry<K, V>> & Serializable)
                (c1, c2) -> cmp.compare(c1.getKey(), c2.getKey());
        }
        public static <K, V> Comparator<Map.Entry<K, V>> comparingByValue(Comparator<? super V> cmp) {
            Objects.requireNonNull(cmp);
            return (Comparator<Map.Entry<K, V>> & Serializable)
                (c1, c2) -> cmp.compare(c1.getValue(), c2.getValue());
        }
    }

在这里插入图片描述

HashMap类

HashMap特点

HashMap类实现了Map接口

HashMap类特点:
1.HashMap底层数据结构是哈希表,当哈希单向链表中元素超过8个时,单向链表这种数据结构会自动变成红黑树数据结构;当红黑树中节点小于6时,会重新将红黑树变成单向链表数据结构
2.非线程安全
3.初始化容量为16,默认加载因子0.75,扩容为原来的2倍
4.HashMap中的Key和Value可以为null

HashMap源码

负载因子

static final float DEFAULT_LOAD_FACTOR = 0.75f;

默认初始化容量

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16

最大容量

static final int MAXIMUM_CAPACITY = 1 << 30;

链表->二叉树 变化阀值

static final int TREEIFY_THRESHOLD = 8;

二叉树->链表 变化阀值

static final int UNTREEIFY_THRESHOLD = 6;

链表节点

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

        Node(int hash, K key, V value, Node<K,V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }

        public final K getKey()        { return key; }
        public final V getValue()      { return value; }
        public final String toString() { return key + "=" + value; }

        public final int hashCode() {
            return Objects.hashCode(key) ^ Objects.hashCode(value);
        }

        public final V setValue(V newValue) {
            V oldValue = value;
            value = newValue;
            return oldValue;
        }

        public final boolean equals(Object o) {
            if (o == this)
                return true;
            if (o instanceof Map.Entry) {
                Map.Entry<?,?> e = (Map.Entry<?,?>)o;
                if (Objects.equals(key, e.getKey()) &&
                    Objects.equals(value, e.getValue()))
                    return true;
            }
            return false;
        }
    }

链表->二叉树 变化源码

/**
         * Forms tree of the nodes linked from this node.
         * @return root of tree
         */
        final void treeify(Node<K,V>[] tab) {
            TreeNode<K,V> root = null;
            for (TreeNode<K,V> x = this, next; x != null; x = next) {
                next = (TreeNode<K,V>)x.next;
                x.left = x.right = null;
                if (root == null) {
                    x.parent = null;
                    x.red = false;
                    root = x;
                }
                else {
                    K k = x.key;
                    int h = x.hash;
                    Class<?> kc = null;
                    for (TreeNode<K,V> p = root;;) {
                        int dir, ph;
                        K pk = p.key;
                        if ((ph = p.hash) > h)
                            dir = -1;
                        else if (ph < h)
                            dir = 1;
                        else if ((kc == null &&
                                  (kc = comparableClassFor(k)) == null) ||
                                 (dir = compareComparables(kc, k, pk)) == 0)
                            dir = tieBreakOrder(k, pk);

                        TreeNode<K,V> xp = p;
                        if ((p = (dir <= 0) ? p.left : p.right) == null) {
                            x.parent = xp;
                            if (dir <= 0)
                                xp.left = x;
                            else
                                xp.right = x;
                            root = balanceInsertion(root, x);
                            break;
                        }
                    }
                }
            }
            moveRootToFront(tab, root);
        }

二叉树->链表 源码

        /**
         * Returns a list of non-TreeNodes replacing those linked from
         * this node.
         */
        final Node<K,V> untreeify(HashMap<K,V> map) {
            Node<K,V> hd = null, tl = null;
            for (Node<K,V> q = this; q != null; q = q.next) {
                Node<K,V> p = map.replacementNode(q, null);
                if (tl == null)
                    hd = p;
                else
                    tl.next = p;
                tl = p;
            }
            return hd;
        }

HashMap的使用

HashMap的三种遍历方式

①通过Key获取Value

    public static void main(String[] args) {
        HashMap<Integer,String> hashMap = new HashMap<>();

        hashMap.put(1,"zhangsan");
        hashMap.put(2,"lisi");
        hashMap.put(3,"wangwu");

        Set<Integer> keySet = hashMap.keySet();     

        for (Integer i: keySet) {
            String s = hashMap.get(i);
            System.out.println("key = " + i +" ---> Value = " + s);
        }
    }

②通过key获得的Set集合,再用Iterator迭代器遍历key,在通过key获取Value

    public static void main(String[] args) {
        HashMap<Integer,String> hashMap = new HashMap<>();

        hashMap.put(1,"zhangsan");
        hashMap.put(2,"lisi");
        hashMap.put(3,"wangwu");

        Set<Integer> keySet = hashMap.keySet();

        Iterator<Integer> iterator = keySet.iterator();

        while(iterator.hasNext()){
            Integer i = iterator.next();
            String s = hashMap.get(i);
            System.out.println("Key = " + i + " ---> Value = " + s);
        }
    }

③使用entrySet方法获取Entry类对象集合,获取Entry类的迭代器,再进行遍历

    public static void main(String[] args) {
        HashMap<Integer,String> hashMap = new HashMap<>();

        hashMap.put(1,"zhangsan");
        hashMap.put(2,"lisi");
        hashMap.put(3,"wangwu");
        
        Set<Map.Entry<Integer,String>> set = hashMap.entrySet();    //获取Entry对象集合

        Iterator<Map.Entry<Integer,String>> iterator = set.iterator();  //获取Entry类迭代器

        while(iterator.hasNext()){
            Map.Entry<Integer,String> entry = iterator.next();
            System.out.println(entry);
        }
    }

HashTable类

HashTable特点

HashTable实现了Map接口

HashTable类的特点:
1.HashTable对象的Key和Value都不能为null
2.HashTable类种方法都是Synchronized修饰的,是线程安全
3.HashTable底层是哈希表数据结构
4.HashTable初始化容量为11默认加载因子是0.75
5.HashT容量满了之后自动扩容为原来的2倍再加一

哈希表(散列表)数据结构

在这里插入图片描述

get方法实现原理
HashTable源码

默认初始化容量11

    /**
     * Constructs a new, empty hashtable with a default initial capacity (11)
     * and load factor (0.75).
     */
    public Hashtable() {
        this(11, 0.75f);
    }

put方法源码和实现原理
V put(K key, V value);
第一步:先将K和V封装成一个Node对象
第二步:底层调用K的hashCode方法得到哈希值(HashCode)
第三步:再通过哈希函数(哈希算法)将HashCode转换成数组下标
若下标位置没有任何元素,就把Node添加到该位置上;
若下标位置有链表,此时拿着K和链表上的每一个节点进行equals方法,如果与该链表上面所有元素比较结果都是false,那么这个新节点将会添加到链表末尾,如果其中一个equals方法返回了true,那么就会用新节点覆盖原节点。

    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;
    }

get方法源码和实现原理
V get(Object key);
第一步:调用K的hashCode方法得到哈希值(HashCode)
第二步:再通过哈希函数(哈希算法)将HashCode转换成数组下标
第三步:用数组下标定位该位置
若此位置上没有元素,则返回null
若此位置上有链表,则对该链表上面的所有元素一个一个进行equals方法,若其中有一个元素的equals方法换回true,则该元素就是我们需要寻找的Value,若所有元素都是返回false,则找不到对应Value,返回null

    public synchronized V get(Object key) {
        Entry<?,?> tab[] = table;
        int hash = key.hashCode();
        int index = (hash & 0x7FFFFFFF) % tab.length;
        for (Entry<?,?> e = tab[index] ; e != null ; e = e.next) {
            if ((e.hash == hash) && e.key.equals(key)) {
                return (V)e.value;
            }
        }
        return null;
    }

Properties类

Properties类特点

Properties类是HashTable类的子类

Properties类的特点:
1.是线程安全的
2.采用Key和Value形式存储数据,并且,两个都是String类型
3.Properties被称为属性类

Properties类使用

需要掌握的方法:
1.Object setProperty(String key, String value)
2.public String getProperty(String key)

setProperty方法测试及源码分析:
setProperty方法底层调用了put方法

源码

    public synchronized Object setProperty(String key, String value) {
        return put(key, value);
    }

getProperty方法测试及源码分析

    public String getProperty(String key) {
        Object oval = super.get(key);
        String sval = (oval instanceof String) ? (String)oval : null;
        return ((sval == null) && (defaults != null)) ? defaults.getProperty(key) : sval;
    }

方法测试

    public static void main(String[] args) {
        //创建Properties对象
        Properties pro = new Properties();

        //存数据:setProperty源码调用了put方法
        //put(key, value)
        pro.setProperty("1","11");
        pro.setProperty("2","22");
        pro.setProperty("3","33");
        pro.setProperty("4","44");
        pro.setProperty("5","55");
        
        //取数据:通过key获取value
        String s1 = pro.getProperty("1");
        String s2 = pro.getProperty("2");
        String s3 = pro.getProperty("3");
        String s4 = pro.getProperty("4");
        String s5 = pro.getProperty("5");

        System.out.println(s1);
        System.out.println(s2);
        System.out.println(s3);
        System.out.println(s4);
        System.out.println(s5);
    }

程序运行结果
在这里插入图片描述

TreeMap类

TreeMap类的特点

TreeMap类继承自AbastractMap类

TreeMap类的特点:
1.无序不可重复
2.遍历输出自动排序
3.底层数据结构是二叉树

TreeMap的使用

    public static void main(String[] args) {
        //创建TreeMap对象
        TreeMap<Integer,String> treeMap = new TreeMap<>();
        //添加元素
        treeMap.put(4,"zhaoliu");
        treeMap.put(1,"zhangsan");
        treeMap.put(2,"lisi");
        treeMap.put(3,"wangwu");
        //获取遍历迭代器
        Set<Map.Entry<Integer,String>> set = treeMap.entrySet();
        Iterator<Map.Entry<Integer,String>> iterator = set.iterator();
        //遍历元素
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

在这里插入图片描述

注意

    public static void main(String[] args) {
        TreeMap<User,Integer> treeMap = new TreeMap<>();

        treeMap.put(new User("zhangsan",10),1);
        treeMap.put(new User("zhangsan",10),2);
        treeMap.put(new User("zhangsan",10),3);
        treeMap.put(new User("zhangsan",10),4);

        //获取遍历迭代器
        Set<Map.Entry<User,Integer>> set = treeMap.entrySet();
        Iterator<Map.Entry<User,Integer>> iterator = set.iterator();
        //遍历元素
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

class User{
    String Name;
    int age;

    public User(String name, int age) {
        Name = name;
        this.age = age;
    }
    public User() {
    }
    @Override
    public String toString() {
        return "User{" +
                "Name='" + Name + '\'' +
                ", age=" + age +
                '}';
    }
}    

Q:上述TreeMap中的元素会自动排列吗?
A:运行之后你会发现有一个异常:java.lang.ClassCastException:Collection.Map.User cannot be cast to java.lang.Comparable
这个异常是由于:传入TreeMap类集合对象中的元素是自定义类对象,程序不知道如何对对象进行比较排序,没有可遵循的排序规则。
Q:那么如何让程序知道排序规则呢?
A:解决方法:①在自定义的类中实现java.lang.Comparable接口,告诉程序如何对自定义类对象的排序规则。②不在类中实现接口,创建TreeMap对象时,传入一个比较器进去

解决方法①

class User implements Comparable {
    String Name;
    int age;

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

    @Override
    public String toString() {
        return "User{" +
                "Name='" + Name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public int compareTo(Object o) {
        User u = (User)o;
        return this.age - u.age;
    }
}

上面的User类实现了Comparable接口,重写改接口中compareTo方法,对User类对象的排序规则为按年龄升序排列

程序运行结果:
在这里插入图片描述

解决方法②

    public static void main(String[] args) {
        TreeMap<User,Integer> treeMap = new TreeMap<>(new UserComparator());

        treeMap.put(new User("zhangsan",10),1);
        treeMap.put(new User("zhangsan",1),2);
        treeMap.put(new User("zhangsan",18),3);
        treeMap.put(new User("zhangsan",20),4);

        //获取遍历迭代器
        Set<Map.Entry<User,Integer>> set = treeMap.entrySet();
        Iterator<Map.Entry<User,Integer>> iterator = set.iterator();
        //遍历元素
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
class User {
    String Name;
    int age;

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

    @Override
    public String toString() {
        return "User{" +
                "Name='" + Name + '\'' +
                ", age=" + age +
                '}';
    }
}

class UserComparator implements Comparator<User>{
    @Override
    public int compare(User o1, User o2) {
        return o1.age - o2.age;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值