java集合常用类详解,涉及底层

一.集合体系图

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

1.Collection常用方法

public class CollectionTest {
    @SuppressWarnings("all")
    public static void main(String[] args) {
        List list = new ArrayList<>();
        //add:添加单个元素
        list.add("jack");
        list.add(10);
        list.add(true);
        System.out.println("list=" + list);
        //Integer a = 10;
        //remove:删除指定元素
        //list.remove(0);
        //list.remove(a);
        System.out.println("list=" + list);
        //contains:查找元素是否存在
        System.out.println(list.contains(10));//返回布尔值
        //size:获取元素个数
        System.out.println(list.size());
        //isEmpty:判断集合是否为空
        System.out.println(list.isEmpty());
        //clear:清空
        //list.clear();
        System.out.println("list="+ list);

        ArrayList<String> list2 = new ArrayList<>();
        list2.add("三国演义");
        list2.add("红楼梦");
        //addAll:添加多个元素
        list.addAll(list2);
        System.out.println("list="+ list);
        //containsAll:查找多个元素是否都存在
        System.out.println(list.containsAll(list2));
        //removeAll:删除多个元素
        list.removeAll(list2);
        System.out.println("list="+ list);

    }
}

2.迭代器与增强for的使用方式

迭代器

public class CollectionIterator {
    public static void main(String[] args) {
        Collection<Book> col = new ArrayList<>();
        col.add(new Book("三国演义","罗贯中",10.1));
        col.add(new Book("小李飞刀","古龙",5.1));
        col.add(new Book("红楼梦","曹雪芹",34.6));

        //1.先得到col对应的迭代器
        Iterator<Book> iterator = col.iterator();
        //2.使用while循环遍历,快捷键魏itit,ctul+j,显示所有快捷菜单
        while (iterator.hasNext()){//判断是否还有数据
            //返回下一个元素,默认返回类型是Object
            Book book = iterator.next();
            System.out.println("book="+ book);

        }
        //3.当推出while循环后,这时迭代器指向最后的元素
        //4.如果希望再次遍历,需要重置迭代器
        //iterator = col.iterator();

    }


}
class Book{
    private String name;
    private String author;
    private double price;

    public Book(String name, String author, double price) {
        this.name = name;
        this.author = author;
        this.price = price;
    }

    public String getName() {
        return name;
    }

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

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Book{" +
                "name='" + name + '\'' +
                ", author='" + author + '\'' +
                ", price=" + price +
                '}';
    }
}

增强for

public class CollectionFor {
    public static void main(String[] args) {
        Collection<Book> col = new ArrayList<>();
        col.add(new Book("三国演义","罗贯中",10.1));
        col.add(new Book("小李飞刀","古龙",5.1));
        col.add(new Book("红楼梦","曹雪芹",34.6));

        //1.使用增强for,在Collection集合,也可以在数组使用
        //2.增强for底层是迭代器
        for (Object book : col){
            System.out.println("book="+ book);
        }
    }
}

3.List接口基本介绍

(1)List接口是Collection接口的子接口

(2)List集合类中元素有序(即添加顺序和取出顺序一致)、且可重复

(3)List集合中的每个元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。

public class List_test {
    public static void main(String[] args) {
        List list = new ArrayList();
        list.add("jack");
        list.add("tom");
        list.add("tom");
        list.add("hsp");
        System.out.println(list);
        System.out.println(list.get(2));
    }
}

List接口基本方法

public class ListMethod {
    @SuppressWarnings("all")
    public static void main(String[] args) {
        List list = new ArrayList<>();
        list.add("南哥");
        list.add("国哥");
        list.add("国哥");
        list.add("国哥");
        //void add(int index, Object ele):在index位置插入ele元素
        list.add(1,"老韩");
        //boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素插入进来
        List list2 = new ArrayList<>();
        list2.add("南哥1");
        list2.add("国哥1");
        list.addAll(1,list2);
        System.out.println(list);
        //Object get(int index):获取指定index位置的元素
        System.out.println(list.get(1));
        //int indexOf(Object obj):返回obj在集合中首次出现的位置
        System.out.println(list.indexOf("国哥"));
        //int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
        System.out.println(list.lastIndexOf("国哥"));
        //Object remove(int index):移除指定index位置的元素,并返回此元素
        System.out.println(list.remove(0));
        System.out.println(list);
        //Object set(int index, Object ele):设置指定index位置的元素ele,相当于是替换。
        list.set(0,"整的");
        System.out.println(list);
        //List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合
        //不包含toIndex
        System.out.println(list.subList(0,2));
    }
}

ArrayList的注意事项及底层原理

(1)Arraylist可以添加一个或多个null。

(2)Arraylist是由数组来实现数据存储的。

(3)Arraylist基本等同于Vector,除了Arraylist是线程不安全的。

(4)ArrayList中维护了一个Object类型的数组elementData,transient Object[] elementData;

(5)当创建对象时,如果使用的是无参构造器,则初始elementData容量为0(jdk7是10)

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
------------------------------------------------------------------------------------------------------------
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

(6)当添加元素时,先判断是否需要扩容,如果需要扩容,则调用grow方法,否则直接添加元素到合适位置

(7)如果使用的是无参构造,如果第一次添加,需要扩容的话,则扩容elementData为10,如果需要再次扩容的话,则扩容 elementData为1.5倍。

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //进行添加元素
    elementData[size++] = e;
    return true;
}

------------------------------------------------------------------------------------------------------------

private void ensureCapacityInternal(int minCapacity) {//minCapacity此时为1
    //判断elementData是否为空数组
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        //private static final int DEFAULT_CAPACITY = 10;
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);//取一个最大值赋给minCapacity
    }
	//再次确认扩容容量
    ensureExplicitCapacity(minCapacity);
}

------------------------------------------------------------------------------------------------------------

 private void ensureExplicitCapacity(int minCapacity) {
        modCount++;//记录当前集合被修改的次数

        //再次确认:当前需要最小容量减去数组当前容量,如果大于零则需扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

------------------------------------------------------------------------------------------------------------
//真正的扩容方法
private void grow(int minCapacity) {
        // elementData.length目前还是0
        int oldCapacity = elementData.length;
    	//相当于oldCapacity*1.5
        int newCapacity = oldCapacity + (oldCapacity >> 1);
    
        if (newCapacity - minCapacity < 0)
            //把minCapacity(为10)赋给newCapacity扩容,所以第一次扩容为10
            newCapacity = minCapacity;
    	//下面if先跳过
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // Arrays.copyOf这里真正进行扩容,这个方法底层还是用System.arraycopy方法
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

(8)如果使用的是指定容量capacity的构造器,则初始elementData容量为capacity

(9)如果使用的是指定容量capacity的构造器,如果需要扩容,则直接扩容elementData为1.5倍

public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

ArrayList与Vector对比

在这里插入图片描述

Vector基本原理

(1)当创建对象时,如果使用的是无参构造器,则初始elementData容量为10

public Vector() {
    this(10);
}

(2)Vector直接以两倍扩容

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    //Vector直接以两倍扩容
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                     capacityIncrement : oldCapacity);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);
}

LinkedList底层结构

(1)LinkedList底层实现了双向链表和双端队列

一、LinkedList维护的双向链表有两个属性,first和last分别指向首、尾节点

二、每个节点(Node对象),里面又维护了prev、next、item三个属性,其中通过prev指向前一个,next指向后一个。最终实现双向链表

三、所以LinkedList元素的添加和删除,不是通过数组完成的,相对来说效率比较高

(2)可以添加任意元素(元素可以重复),包括null,默认尾插

(3)线程不安全,没有实现同步

LinkedList.add方法

public boolean add(E e) {
    linkLast(e);
    return true;
}

------------------------------------------------------------------------------------------------------------

void linkLast(E e) {//e:1
        final Node<E> l = last;//lastc第一次添加时为空,这句重点在后续添加
        final Node<E> newNode = new Node<>(l, e, null);//创建一个新节点存储e,如果前面有节点则将prev指向前一个
        last = newNode;//将last指向newNode
        if (l == null)
            first = newNode;//将first指向newNode
        else
            l.next = newNode;//这句重点在后续添加
        size++;//记录集合大小,使用get方法时用来循环遍历集合
        modCount++;//记录修改次数
    }

LinkedList.size方法

public int size() {
    return size;
}

LinkedList.get方法

public E get(int index) {
    checkElementIndex(index);//判断index是否越界
    return node(index).item;//返回index对应item
}

private void checkElementIndex(int index) {
        if (!isElementIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isElementIndex(int index) {
        return index >= 0 && index < size;
}

------------------------------------------------------------------------------------------------------------
Node<E> node(int index) {
        // assert isElementIndex(index);
		//这个看不懂
        if (index < (size >> 1)) {
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {
            //通过下面循环从尾部遍历拿到index对应元素并返回
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }

linkedList.remove()方法

无参构造默认删除头节点,也可以输入下标或值来删除对应节点

public E remove() {
    return removeFirst();
}
//将f指向头节点,并判断是否为空,在调用unlinkFirst把f传进去
public E removeFirst() {
        final Node<E> f = first;
        if (f == null)
            throw new NoSuchElementException();
        return unlinkFirst(f);
}

private E unlinkFirst(Node<E> f) {
        //拿到f对应element,next,再将原本头节点清空,以便回收
        final E element = f.item;
        final Node<E> next = f.next;
        f.item = null;
        f.next = null; // help GC
        first = next;//将first指向原本头节点的下一个,使之成为头节点
        if (next == null)//判断next是否为空,如果为空则清空集合
            last = null;
        else
            next.prev = null;//将next的prev指向清空
        size--;
        modCount++;
        return element;
}

ArrayList与linkedList对比

在这里插入图片描述

4.List接口实现子类的三种遍历方式

Iterator<Object> iterator = linkedList.iterator();
while (iterator.hasNext()){
    Object next = iterator.next();
    System.out.println(next);
}

for(Object list : linkedList){
    System.out.println(list);
}

for (int i = 0; i < linkedList.size(); i++) {
    System.out.println(linkedList.get(i));
}

5.set接口基本介绍

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

(2)不允许重复元素,所以只能有一个null

(3)遍历方式不能使用fori,可以用迭代器与增强for

public class HashSet01 {
    public static void main(String[] args) {
        Set set = new HashSet<>();
        set.add("lucy");//添加成功
        set.add("lucy");//添加失败
        set.add(new String("lucy"));//添加失败
        set.add(new Cat("tom"));//添加成功
        set.add(new Cat("tom"));//添加成功
        set.add(new String("h"));//添加成功
        set.add(new String("h"));//添加失败
        System.out.println(set);
    }
}
class Cat{
    private String name;

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

    @Override
    public String toString() {
        return "Cat{" +
                "name='" + name + '\'' +
                '}';
    }
}

HashSet存储结构简单模拟

public class HashSetStructure {
    public static void main(String[] args) {
        //简单模拟一个HashSet的底层(就是HashMap的底层结构)
        //数组+链表,这是不完整的
        Node[] table = new Node[16];
        Node john = new Node("john", null);
        table[2] = john;
        Node rose = new Node("Rose", null);
        john.next = rose;
        Node jack = new Node("jack", null);
        rose.next = jack;

        Node lucy = new Node("lucy", null);
        table[3] = lucy;
        System.out.println(table);
    }
}
class Node{ //节点,存放数据,可以指向下一个节点,从而形成链表
    Object item; //存放数据
    Node next; //指向下一个节点
    public Node(Object item, Node next) {
        this.item = item;
        this.next = next;
    }
}

HashSet底层机制说明

(1)HashSet底层是HashMap,第一次添加时,table数组扩容到16,临界值为table.size*0.75,数组使用大于临界值就会进行扩容

(2)添加一个元素时,先得到hash值,会转成索引值

(3)找到存储数据表table,看这个索引位置是否已经存放有元素,如果没有,直接加入;如果有,调用equals比较,如果相同,就放弃添加,如果不相同,则添加到最后。(equals比较什么内容可以由程序员来定,可以重写equals)

(4)在Java8中,如果一条链表的元素个数>=TREEIFY_THRESHOLD(默认是8),并且table的大小 >= MIN_TREEIFY_CAPACITY(默认64),就会进行树化(红黑树)否则会继续扩容知道满足条件再树化,注意:在把元素添加到链表后,立即判断该链表是否已经>=8个节点

(5)table数组长度最大应该为0~99

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

------------------------------------------------------------------------------------------------------------

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

------------------------------------------------------------------------------------------------------------

//执行put方法会执行hash(key),得到key对应的hash值(不等于hashCode,因为做了运算)算法(h = key.hashCode()) ^ (h >>> 16)
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就是HashMap的一个数组,类型是Node[]
    	//table初始为null,条件成立,if语句表示如果当前table == null,或者大小 == 0
    	//就是第一次扩容到16个空间
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
    	//根据key,得到hash,去计算该key应该存放到table表的哪个索引位置
    	//tab[i = (n - 1) & hash]),n为当前数组大小,再与当前元素hash值进行&运算得到位置索引i
    	//并把这个位置的对象,赋给p,再判断p是否为null
    	//如果p为null,表示这个索引位置还没有存放元素,就创建一个Node存放进去
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            //判断当前添加的元素与集合中对应索引的元素hash是否相同
            //再判断key是否相同,这里同时用了==和equals进行比较,重点是理解这里。final K key;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                //for:如果该索引位置已有元素,则进行循环比较此索引链表,再进行添加或不添加 
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        //注意:在把元素添加到链表后,立即判断该链表是否已经>=8个节点
                        //trun:就调用treeifyBin(tab, hash)对当前这个链表进行树化(转红黑树)
                        //树化条件:table>=64,链表节点>=8,否则先进行扩容满足条件再树化
                        
                        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;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;//如果重复添加就替换value值
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;//记录修改次数
    	//判断size是否大于临界值,大于就进行扩容
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);//留给HashMap子类实现一些功能
        return null;//返回null相当于添加成功了
    }

------------------------------------------------------------------------------------------------------------

final Node<K,V>[] resize() {
        Node<K,V>[] oldTab = table;
        int oldCap = (oldTab == null) ? 0 : oldTab.length;
        int oldThr = threshold;
        int newCap, newThr = 0;
        if (oldCap > 0) {
            if (oldCap >= MAXIMUM_CAPACITY) {
                threshold = Integer.MAX_VALUE;
                return oldTab;
            }
            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
            newCap = oldThr;
        else {//static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;               
            newCap = DEFAULT_INITIAL_CAPACITY;//16
            //static final float DEFAULT_LOAD_FACTOR = 0.75f
            //这是一个临界值,大于这个值就会开始扩容
            newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
        }
        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;
    }

LinkedHashSet基本说明

(1)LinkedHashSet是HashSet的子类

(2)LinkedHashSet底层是一个LinkedHashMap,底层维护了一个数组+双向链表

(3)LinkedHashSet根据元素的hashCode值来决定元素的存储位置,同时使用链表维护元素的次序,这使得元素看以来是以插入顺序保存的

(4)LinkedHashSet不允许添加重复元素,第一次添加时,直接将数组table扩容到16,

(5)数组是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);
    }
}

6.Map接口

Map接口常用方法

(1)put:添加

(2)remove:根据键删除映射关系

(3)get:根据键获取值

(4)size:获取元素个数

(5)isEmpty:判断个数是否为0

(6)clear:清除

(7)containsKey:查找键是否存在

Map接口实现类的特点

(1)Map用于保存具有映射关系的数据:Key-Value

(2)Map中的Key和Value可以是任何引用类型数据,会封装到HashMap$Node对象中

(3)Map中的Key不可以重复,Value可以重复,如果Key重复则会替换Value

if (!onlyIfAbsent || oldValue == null)
    e.value = value;//如果重复添加就替换value值

(4)Map中的Key可以为null,value也可以为null,key为null,只能有一个,value可以有多个null

(5)常用String类作为Map的key,也可以使用Object或子类

(6)key和value之间存在单向一对一关系,即通过key总能找到对应的value

Map 一> EntrySet理解(重难点)

public class MapSource_ {
    public static void main(String[] args) {
        Map map = new HashMap<>();
        map.put("no1","韩");
        map.put("no2","张");
        //1. k-v 最后是HashMap$Node node = newNode(hash, key, value, null)
        //2. k-v 为了方便程序员的遍历,还会创建EctrySet集合,该集合存放的元素的类型是Entry,
        //而一个Entry对象就有k,v EntrySet<Entry<k,v>> 即transient Set<Map.Entry<k,v>> entrySet;
        //3.entrySet中,定义的类型是Map.Entry,但是实际上存放的还是HashMap$Node
        //  这是因为static class Node<k,v> implements Map.Entry<k,v>
        //4.当把 HashMap$Node 对象存放到 EntrySet 就方便遍历,因为 Map.Entry 提供了重要方法
        //  K getKey(); V getValue();

        Set set = map.entrySet();

        System.out.println(set.getClass());//HashMap$EntrySet
        for (Object o : set) {
            //System.out.println(o.getClass());//HashMap$Node
            Map.Entry entry = (Map.Entry) o;
            System.out.println(entry.getKey() + "-" + entry.getValue());
        }
    }
}

Map六种遍历方式

public class mapFor {
    public static void main(String[] args) {
        Map map = new HashMap<>();
        map.put("no1","韩");
        map.put("no2","张");

        Set keyset = map.keySet();
        //1.增强for
        for (Object o : keyset) {
            System.out.println(o + "-" + map.get(o));
        }
        System.out.println("--------------------");
        //2.迭代器
        Iterator iterator = keyset.iterator();
        while (iterator.hasNext()) {
            Object next = iterator.next();
            System.out.println(next + "-" + map.get(next));
        }
        System.out.println("--------------------");
        //3.取出所有的Values
        Collection values = map.values();
        //这里可以使用所有Collection的遍历方法
        //增强for
        for (Object value : values) {
            System.out.println(value);
        }
        System.out.println("------迭代器----------");
        //迭代器
        Iterator iterator1 = values.iterator();
        while (iterator1.hasNext()) {
            Object next = iterator1.next();
            System.out.println(next);
        }
        System.out.println("--------entrySet--------");
        //4.通过EctrySet来获取K-V
        Set entrySet = map.entrySet();//EntrySet<Map.Entry<K,V>>
        //(1)增强for
        for (Object entry : entrySet) {
            Map.Entry m = (Map.Entry) entry;
            System.out.println(m.getKey() + "-" + m.getValue());
        }
        System.out.println("--------迭代器--------");
        //迭代器
        Iterator iterator2 = entrySet.iterator();
        while (iterator2.hasNext()) {
            Object next = iterator2.next();
            Map.Entry m = (Map.Entry) next;
            System.out.println(m.getKey() + "-" + m.getValue());
        }
    }
}

HashMap底层机制在HashSet已讲,这里不再赘述

HashTable基本介绍

(1)存放的元素是键值对:k-v

(2)hashtable的键和值都不能为null

(3)hashTable使用方法基本上与HashMap一样

(4)hashTable是线程安全的(synchronized),HashMap是线程不安全的

(5)底层数组Hashtable$Entry[ ] 初始化大小为11,临界值为11*0.75,扩容机制为size * 2 + 1

public synchronized V put(K key, V value) {
        //执行以下代码如果value都为null,则抛异常
        if (value == null) {
            throw new NullPointerException();
        }

        // Makes sure the key is not already in the hashtable.
        Entry<?,?> tab[] = table;
        //执行以下代码如果key为null,则抛异常
        int hash = key.hashCode();
    	//计算存储索引
        int index = (hash & 0x7FFFFFFF) % tab.length;
        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;
    }


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

        Entry<?,?> tab[] = table;
        if (count >= threshold) {
            // 预扩容方法
            rehash();

            tab = table;
            hash = key.hashCode();
            index = (hash & 0x7FFFFFFF) % tab.length;
        }
		//创建元素并插入,使用头插
        Entry<K,V> e = (Entry<K,V>) tab[index];
        tab[index] = new Entry<>(hash, key, value, e);
        count++;
    }

总结-(开发中如何选择集合实现类)

在开发中,选择什么集合实现类,主要取决于业务操作特点,然后根据集合实现类特性进行选择。

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

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

​ 允许重复:List

​ 增删多:LinkedList[底层维护了一个双向链表]

​ 改查多:ArrayList[底层维护Object类型的可变数组]

​ 不允许重复:Set

​ 无序:HashSet[底层是HashMap,维护了一个哈希表,即(数组+链表+红黑树)]

​ 排序:TreeSet

​ 插入和取出顺序一致:LinkedHashSet,维护数组+双向链表

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

​ 键无序:HashMap[底层是:哈希表: 数组+链表+红黑树]

​ 键排序:TreeMap

​ 键插入和取出顺序一致:LinkedHashMap

​ 读取文件:Properties

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值