02_5_Java集合

1 数组与集合

1.1 集合与数组存储数据概述

  • 集合、数组都是对多个数据进行存储操作的结构,简称Java容器。

    说明:此时的存储,主要是指的是内存层面的存储,不涉及到持久化的存储。

1.2数组存储特点

  • 一旦初始化后,其长度就确定了。

  • 数组一旦定义好,其元素类型也就确定了。我们只能操作指定类型的数据。

    String[] arr = new String[4];//数组类型和长度确定
    String[] arr2  = new String[]{"123", "AA", "cc"};//数组类型和长度确定
    int[] arr3;
    Object[] arr4;
    

1.3 数组存储的弊端

①一旦初始化后,其长度就不可修改

②数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便,同时效率不高。

③获取数组中实际元素的个数的需求,数组中没有现成的属性或方法可用。

④数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足。

1.4集合存储的优点

解决数组存储数据方面的弊端

2 Collection接口

2.1 单列集合框架结构

/----Collection接口:单列集合,用来存储一个一个的对象

​ /----list接口:存储有序的、可重复的数据。 —>“动态的数组”

​ /----ArrayList、LinkedList、Vector

​ /----Set接口:存储无序的、不可重复的数据 —>高中的集合

​ /----HashSet、LinkedHashSet、TreeSet

1598233844083

2.2 Collection接口常用方法

  • 有15个

add(Object obj)添加元素

addAll(Collection coll)将列表coll中的元素全部添加到当前列表

size()当前列表内的元素个数

isEmpty()判断当前列表是否为空

clear()清空当前列表

remove(Object obj) 删除元素

removeAll(Collection col)

removeIf

contains(Object obj)判断是否包含元素obj,返回boolean

containsAll(Collection coll)判断当前集合是否包含集合coll,返回boolean

retainAll(Collection coll),取当前集合和coll的交集,赋给当前集合,有返回值,返回值的类型为boolean

equals()判断两个集合是否相等

hasCode(),返回当前集合的哈希值

toArray(),列表转数组,返回一个Objcet类型的数组

iterator(),返回一个当前集合的迭代器

2.3 Collection集合与数组之间的转换

集合—>数组:toArray()
        //toArray()集合转数组,转换成了一个Object类型的数组
        Object[] objects = coll1.toArray();
		//foreach循环(增强for循环)
        for (Object o : objects) {
            System.out.println(o);
        }
数组—>集合:调用Arrays类的静态方法asList(T … t0)
        //数组转集合
        List<String> strings = Arrays.asList(new String[]{"AA", "BB", "CC"});
        System.out.println(strings);

        //需要注意的是!基本数据类型的数组,为被认为是一个元素~
        //所以如果要解决这个问题,需要使用包装类
        List<int[]> ints = Arrays.asList(new int[]{123, 333, 90});
        System.out.println(ints);

        List<Integer> integers = Arrays.asList(new Integer[]{11, 22, 34});
        System.out.println(integers);

2.4 Collection集合存储对象的注意点

  • 向Collection接口的实现类的对象中添加obj时,要求obj所在类要重写equals()

2.5 删除Collection元素的三种方式

        Collection collection = new ArrayList();
        collection.add("java");
        collection.add("bigdata");
        collection.add("python");
        collection.add("bigdata");
        collection.add("bigdata");
//方式一:iterator.remove()
//区别于:collection.remove(Object obj)
		Iterator iterator = collection.iterator();
        while (iterator.hasNext()){
            if (iterator.next().equals("bigdata")){
                iterator.remove();
            }
        }
//方式二:contains(Object obj)
		while(collection.contains("bigdata")){
            collection.remove("bigdata")
        }
//方式三:removeAll(Collection coll)
		Collection coll = new ArrayList();
		coll.add("bigdata");
		collection.removeAll(coll);

3 Iterator接口与foreach循环

3.1 遍历Collection的两种方式

  • 使用迭代器Iterator

    Iterator iterator = coll.iterator();
    //遍历集合
    while(iterator.hasNext()){
        System.out.println(iterator.next());
    }
    
  • 使用foreach循环

    //快捷键时iter
            Object[] objects = coll1.toArray();
            for (Object o : objects) {
                System.out.println(o);
            }
    

3.2 Iterator迭代器

  • java.utils包下定义的迭代器接口:Iterator
  • Iterator对象称为迭代器(设计模式中的一种),主要用于遍历Collection集合中的元素。
  • 1598244426456
iterator迭代器的源码分析
        Collection collection = new ArrayList();
        collection.add("java");
        collection.add("bigdata");
        collection.add("python");
        collection.add("bigdata");
        collection.add("bigdata");

        Iterator iterator = collection.iterator();
        while (iterator.hasNext()){
            if (iterator.next().equals("bigdata")){
                iterator.remove();
            }
        }
//iterator()
/*
Iterator 是一个接口,coll调用iterator(),生成一个iterator迭代器是调用ArrayList内的iterator()方法,return new Itr();返回一个Itr对象,Itr实现了Iterator接口;Itr是ArrayList内的一个内部类,它有三个属性,cursor返回值的游标索引值、lastRet辅助游标cursor-1;modCount操作数。

源码:
	Collection coll = new ArrayList();
    public Iterator<E> iterator() {
        return new Itr();
    }
        private class Itr implements Iterator<E> {
        int cursor;       // 游标,从0开始
        int lastRet = -1; // 游标上一个,从-1开始
        int expectedModCount = modCount;//操作的次数

*/

//hasNext()
/*
运行时看ArrayList的hasNext();判断游标索引和有效个数。
源码:
        public boolean hasNext() {
            return cursor != size;
        }
*/

//next()
/*
ArrayList的iterator的next方法,
源码:
        public E next() {
            checkForComodification();//检查异常
            	
            	checkForComodifcation()的内部源码
            	final void checkForComodification() {
           			 if (modCount != expectedModCount)//判断集合的操作数和迭代器的操作数是否相等。
              		 throw new ConcurrentModificationException();
        		}
            	
            	
            int i = cursor;//定义一个辅助游标,帮助遍历数组;判断的目的
            if (i >= size)//size数组的有限数据的个数
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;//Object[] elementData的地址指向将coll的数组的地址
            if (i >= elementData.length)//length数组的实际长度,判断的目的是
                throw new ConcurrentModificationException();
            cursor = i + 1;//游标先下移,再返回上一个值。
            return (E) elementData[lastRet = i];//返回游标的上一个值。
        }
*/
//iterator.remove()
/*
源码:
        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);//迭代器中的删除还是调用ArrayList中的remove()
                cursor = lastRet;//删除一个后游标后移到lastRet位置。
                lastRet = -1;
                expectedModCount = modCount;//这是不报错的主要原因!
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

*/

//coll.remove()
/*
    public E remove(int index) {
        rangeCheck(index);

        modCount++;//Collection中的删除操作数++,但是在上述checkForComodification(),导致next方法中的迭代器操作数与之不相等,会报异常。
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

*/

  • 迭代器中remove()的使用

    • 如果还没调用next()或在上一次调用next方法之后,已经调用了remove方法,再调用remove都会报IllegalStateException
    • 迭代器中定义的remove方法不同于Collection集合直接调用remove。
@Test
public void test1{
    Collection coll = new ArrayList();
    coll.add(123);
    coll.add(456);
    coll.add(new Person("Jerry",20));
    coll.add(new String("Tom"));
    coll.add(false);
    
    //删除集合中"Tom"
    Iterator iterator = coll.iterator();
    while(iterator.hasNext()){
        Object obj = iterator.next();
        if("Tom".equals(obj)){
            //调用iterator的remove()
            iterator.remove();
        }
    }
    //遍历集合
    iterator = coll.iterator();
    while(iterator.hasNext()){
        System.out.println(iterator.next());
    } 
}

3.3 foreach循环(增强for循环)

  • jdk 5.0 新特性 – 增强for循环(foreach循环)

  • 遍历集合举例:

    @Test
    public void test1(){
        Collection coll = new ArrayList();
        coll.add(123);
        coll.add(456);
        coll.add(new Person("Jerry",20));
        coll.add(new String("Tom"));
        coll.add(false);
    
        //for(集合元素的类型 局部变量 : 集合对象)
        
        for(Object obj : coll){
            System.out.println(obj);
        }
    }
    
  • 说明

    内部仍然时调用了迭代器

  • 遍历数组举例:

    @Test
    public void test2(){
        int[] arr = new int[]{1, 2, 3, 4, 5};
        //for(数组元素的类型 局部变量:数组对象)
        for(int i : arr){
            System.out.println(i);
        }  
    }
    

4 Collection子接口:List接口

4.1 存储的数据的特点

存储有序的、可重复的数据

4.2 常用方法(记住!!!)

  • 增:add(Object obj)

  • 删:remove(Object obj) / remove(int index)

  • 改:set(int index, Object obj)

  • 查:get(int index)

  • 插:add(int index, Object obj)

  • 长度:size()

  • 遍历:①Iterator迭代器方式

    ​ ②增强for循环

    ​ ③普通for循环

  • 增加:add(Object obj) | add(int index, Object obj) | addAll(Collection coll) | addAll(int index, Collection coll)

    • 注意:add(int index, Object obj),index下标索引值可以=size;但是不能间隔一个去插入

              List list = new ArrayList();
              list.add("java");
              list.add(123);
              list.add("123L");
              list.add(3, "hadoop");//这可以
              list.add(8, "spark");//IndexOutOfBoundsException
      
  • 删除:remove

  • 修改

4.3 常用实现类

/----Collection接口:单列集合,用来存储一个一个的对象

​ /----List接口:存储有序的、可重复的数据。–>”动态数组“,替换原来的数组

​ /----ArrayList:作为List接口的主要实现类;线程不安全,效率高;底层使用Object[] elementData存储

​ /----LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储

4.4 源码分析(难点)

ArrayList的源码分析
  • jdk 7 的情况下

    1. ArrayList list = new ArrayList();//底层创建了长度为10Object[] 数组elementData;

    2. list.add(123);//elementData[0] = new Integer(123);

      list.add(11);//如果此次的添加导致底层的elementData数组容量不够,则扩容。

      • 默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。
    3. 结论:建议开发中使用带参数的构造器:

      ArrayList list = new ArrayList(int capacity)

  • jdk 8 的情况下

    1. ArrayList list = new ArrayList(); //底层Object[] elementData初始化为{}。并没有创建长度为10的数组。

    2. list.add(123);//第一次调用add()时,底层才创建了长度为10的数组,并将数据123添加到elementData[0]

      后续的添加和扩容操作与jdk 7 无异

    3. 结论:jdk 7的ArrayList的对象的创建相当于类似单例的饿汉式,而jdk 8的ArrayList的对象的创建相当于懒汉式,延迟了数组的创建,节省内存。

//ArrayList的源码分析
    @Test
    public void testArrayList(){
        List list = new ArrayList();
        //增add(Object obj)
        list.add("java");
        list.add(123);
        list.add(33);
        list.add(14423);
        list.add("123L");
        list.add("123L");
        list.add("123L");
        list.add(5, "hadoop");
        list.add("123L");
        list.add("123L");
        list.add("123L");
        System.out.println(list);
    }

/*
ArrayList里的属性:
	this.elementData,是ArrayList的实例化对象的数组。
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//jdk1.8以后,默认初始的数组为空。在第一次添加的时候才扩容为10。
   1. 初始化,new ArrayList();
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;//
    }
   2. 第一次添加的时候:
       public boolean add(E e) {
        ensureCapacityInternal(size + 1);//2.1,minCapacity = size + 1 //1
        elementData[size++] = e;
        return true;
    }
    			2.1 
    			private void ensureCapacityInternal(int minCapacity) {
    				//第一次添加的时候elementData==DEFAULT...都是空数组
                    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                    	//最小容量 = max(10, 1) = 10
                        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
                    }
                    ensureExplicitCapacity(minCapacity);2.2
                }
                	2.2
                    private void ensureExplicitCapacity(int minCapacity) {
                        modCount++;//操作数++
                        //如果最小容量 > 数组长度 ---> 10 > 0
                        if (minCapacity - elementData.length > 0)
                            grow(minCapacity);//2.3 扩容机制
                    }
                    	2.3
                        private void grow(int minCapacity) {
                        int oldCapacity = elementData.length;
                        int newCapacity = oldCapacity + (oldCapacity >> 1);
                        if (newCapacity - minCapacity < 0)//第一次添加的时候
                            newCapacity = minCapacity;
                        if (newCapacity - MAX_ARRAY_SIZE > 0)//数组容量特别大的时候
                            newCapacity = hugeCapacity(minCapacity);
                        elementData = Arrays.copyOf(elementData, newCapacity);//数组扩容
                    }
*/
LinkedList的源码分析
  1. LinkedList list = new LinkedList();内部声明了Node类型的first和last属性,属性值为null。

  2. List.add(123);//将123封装到Node中,创建了Node对象。

    • 其中,Node定义为:体现了LinkedList的双向链表的说话
//LinkedList的源码分析
private static class Node<E> {
            E item;
            Node<E> next;
            Node<E> prev;

            Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
            }
        }
//LinkedList的源码分析
    @Test
    public void testLinkedList(){
        List list = new LinkedList();
        //增add(Object obj)
        list.add("hadoop");
        list.add(1024);
        list.add("spark");
        System.out.println(list);
    }
/*
1. new LinkedList(),实例化LinkedList对象
    public LinkedList() {//空参构造器
    }
    1.1 LinkedList的属性
    	    transient int size = 0;
    	    transient Node<E> first;//first和last都是Node类型的节点。LinkedList中只保存了头结点和尾节点的两个属性;还有size节点数量。
    	    transient Node<E> last;
2. add()添加操作。
    public boolean add(E e) {
        linkLast(e);//2.1 添加元素
        return true;
    }
    2.1 
        void linkLast(E e) {
            final Node<E> l = last;//定义一个辅助节点,保存last节点;
            final Node<E> newNode = new Node<>(l, e, null);//声明一个newNode节点。
            last = newNode;//将链表中的last=newNode
            if (l == null)//第一次添加的时候l = null,first = newNode
                first = newNode;
            else//不是第一次添加的时候,l.next = newNode
                l.next = newNode;
            size++;//每添加一个size++
            modCount++;//每添加一个操作数++
        }

*/
Vector的源码分析

jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组。

在扩容方面,默认扩容为原来的数组长度的2倍。

//Vector的源码分析
@Test
    public void testVector(){
        List list = new Vector();
        list.add("hadoop");
        for (int i = 0; i < 9; i++) {
            list.add("i" + i);
        }
        list.add(123);
        System.out.println(list);
    }
/*
源码:
1. new Vector(),创建Vector对象
    public Vector() {
        this(10);//默认初始容量为10;
    }
2. add(),添加元素
    public synchronized boolean add(E e) {
        modCount++;//操作数++
        ensureCapacityHelper(elementCount + 1);// 2.1判断是否需要扩容
        elementData[elementCount++] = e;//添加元素到数组
        return true;
    }
    		2.1 
    	    private void ensureCapacityHelper(int minCapacity) {
                if (minCapacity - elementData.length > 0)
                    grow(minCapacity);//扩容操作,第一次添加不需要扩容
            }
3. 添加到第11个时,需要扩容,扩容为原容量的两倍
    private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        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);
    }
4. 另外需要说明的是,Vector的大部分方法是synchronize的,所以线程安全。
*/

4.5 存储的元素的要求

添加的对象,所在的类要重写equals()方法

[面试题]

  • 面试题:ArrayList、LinkedList、Vector者的异同?
  • 同:三个类都是实现了List接口,存储数据的特点相同:存储序的、可重复的数据
  • 不同:见上(第3部分+第4部分)

5 Collection子接口:Set接口

5.1 存储的数据特点

无序的、不可重复的元素。

  • 以HashSet为例说明:

    1. 无序性:不等于随机性。存储的数据在底层数组中并非照数据索引的顺序添加,而是根据数据的哈希值决定的。
    2. 不可重复性:保证添加的元素照equals()判断时,不能返回true。即相同的元素只能添加一个。

5.2 元素添加过程(以HashSet为例)

  1. 我们向HashSet中添加元素a,首先会调用元素a所在类的hashCode()方法,计算元素a的哈希值。

  2. 此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为索引位置)

  3. 判断数组位置上是否已经有元素:

    3.1 如果此位置上没有其他元素,则元素a添加成功。---->情况1

    3.2 如果此位置上有其他元素b(或以链表的形式存在多个元素),则比较元素a与元素b的hash值:

    ​ 3.2.1 如果hash值不相同,则元素a添加成功。---->情况2

    ​ 3.2.2 如果hash值相同,进而需要调用元素a所在类的equals()方法:

    ​ 3.2.2.1 equals()返回true,元素a添加失败

    ​ 3.2.2.2 equals()返回false,元素a添加成功。---->情况3

  • 对于jdk 7 和jdk 8 添加成功的情况2和情况3而言:

    元素a与已经存在指定索引位置上的数据以链表的方式存储

    jdk7 : 元素a放到数组中,指向原来的元素。

    jdk8: 原来的元素在数组中,指向元素a

    总结:七上八下

  • HashSet底层:数组+链表的结构

5.3 常用方法

Set接口中没有额外定义新的方法,使用的都是Collection中声明的方法。

5.4 常用实现类

/----Collection接口:单列集合,用来存储一个一个的对象

​ /----Set接口:存储无序的、不可重复的数据—>高中讲的集合

​ /----HashSet:作为Set接口的主要实现类;线程不安全;可以存储null值;

​ /----LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据。

​ /----TreeSet:可以按照添加对象的指定属性,进行排序。

1598318214901

5.5 存储对象所在类的要求

  • HashSet/LinkedHashSet

    • 要求:向Set(主要指:HashSet、LinkedHashSet)中添加的数据,其所在的类一定要重写hashCode()和equals()

    • 要求:重写的hashCode()和equals()尽可能保持一直;相等的对象必须具有相等的散列码

      • 重写两个方法的小技巧:对象中用作equals()方法比较的Field,都应该用来计算hashCode值。
  • TreeSet:

    • 自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,不再是equals()
    • 定制排序中,比较两个对象是否相同的标准为:compare()返回0,不再是equals()

5.6 TreeSet的使用

使用说明
  • 向TreeSet中添加的数据,要求是相同类的对象
  • 两种排序方式:自然排序(实现Comparable接口 和 定制排序Comparator)
常用的排序方式
  • 方式一:自然排序
@Test
    public void test1(){
        TreeSet set = new TreeSet();

        //失败:不能添加不同类的对象
//        set.add(123);
//        set.add(456);
//        set.add("AA");
//        set.add(new User("Tom",12));

            //举例一:
//        set.add(34);
//        set.add(-34);
//        set.add(43);
//        set.add(11);
//        set.add(8);

        //举例二:
        set.add(new User("Tom",12));
        set.add(new User("Jerry",32));
        set.add(new User("Jim",2));
        set.add(new User("Mike",65));
        set.add(new User("Jack",33));
        set.add(new User("Jack",56));


        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }

    }
  • 方式二:定制排序
@Test
    public void test2(){
        Comparator com = new Comparator() {
            //照年龄从小到大排列
            @Override
            public int compare(Object o1, Object o2) {
                if(o1 instanceof User && o2 instanceof User){
                    User u1 = (User)o1;
                    User u2 = (User)o2;
                    return Integer.compare(u1.getAge(),u2.getAge());
                }else{
                    throw new RuntimeException("输入的数据类型不匹配");
                }
            }
        };

        TreeSet set = new TreeSet(com);
        set.add(new User("Tom",12));
        set.add(new User("Jerry",32));
        set.add(new User("Jim",2));
        set.add(new User("Mike",65));
        set.add(new User("Mary",33));
        set.add(new User("Jack",33));
        set.add(new User("Jack",56));


        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

6 Map接口

Map - 双列集合框架

6.1 常用实现类结构

/----Map:双列数据,存储key-value对的数据。—> 类似高中的函数:y = f(x)

​ /----HashMap:作为Map的主要实现类:线程不安全的,效率高;可以存储null的key和value

​ HashMap底层使用数组+链表(jdk7及以前);数组+链表+红黑树(jdk8)

​ /----LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序实现遍历。

​ 原因:在原来的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。对于频繁的遍历操作,此类执行效率高于HashMap。

​ /----TreeMap:保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或定制排序。

​ TreeMap底层使用红黑树结构

​ /----Hashtable:作为古老的实现类:线程安全的,效率低,不能存储null的key和value

​ /----Properties:常用来处理匹配文件。key和value都是String类型。

【面试题】

  • HashMap的底层实现原理?
  • HashMap和Hashtable的异同?
  • CurrentHashMap与Hashtable的异同?(暂时不讲)

6.2 存储结构的理解

  • Map中的key:无序的、不可重复的,使用Set存储的key

    ​ key所在的类要重写equals()和hashCode() (以HashMap为例)

  • Map中的value:无序的、可重复的,使用Collection存储value

    value所在的类要重写equals()

  • Map中的entry:无序的、可重复的,使用Set存储entry

    一个键值对:key-value 构成一个Entry对象

1598401508502

6.3 常用的方法

  • 添加:put(Object key, Object value)

    ​ putAll(Map map)

  • 删除:remove(Object key)

    ​ remove(Key k, Value v)

  • 修改:replace(Key k, newValue)

    ​ replace(Key k, oldValue, newValue)

  • 查询:get(Object key)

    ​ containsKey(Key k)

    ​ containsValue(Value v)

  • 遍历:keySet() —> 遍历key值,使用iterator或foreach

    ​ values() —> 遍历value值,使用iterator或foreach

    ​ entrySet() —> 遍历entry

6.4 内存结构说明(难点)

HashMap在jdk7中实现原理
  1. HashMap map = new HashMap():

    在实例化以后,底层创建了长度是**16**的一维数组Entery[] table

    …可能执行过多次put…

    map.put(key1, value1);

  2. 首先调用key1所在类的hashCode()计算key1的哈希值,此时经过某种算法计算后,得到在Entry数组中的存放位置。

    2.1 如果此位置上的数据为空,此时的key1-value1添加成功。 —> 情况1

    2.2 如果此位置上的数据不为空,(意味着此位置上存在一个或多个数据)(以链表形式存在)),比较key1和已经存在的一个或多个数据的哈希值:

    ​ 2.2.1 如果key1的哈希值与已经存在的数据的哈希值不都相同,此时key1-value1添加成功。 —> 情况2

    ​ 2.2.2 如果key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2)方法,比较:

    ​ 2.2.2.1 如果equals()返回false:此时key1-value1添加成功。

    ​ 2.2.2.2 如果equals()返回true:使用value1替换value2

  • 补充:关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储。
  • 在不断的添加过程中,会涉及到扩容的问题,当超出临界值(且要存放的位置非空)时,扩容。
  • 默认的扩容方式:扩容为原来容量16的两倍,将原来的数据复制过来。
HashMap在jdk8中相较于jdk7在底层实现方面的不同
  1. new HashMap():底层没有创建一个长度为16的数组
  2. jdk 8 底层的数组是:Node[],而非Entry[]
  3. 首次调用put()方法时,底层创建长度为16的数组
  4. jdk7底层结构:数组+链表;jdk8底层结构:数组+链表+红黑树
    • 形成链表时,七上八下(jdk7:新的元素指向旧的元素;jdk8:旧的元素指向新的元素)
    • 当数组的某一个索引位置上的元素以链表形式存在的数据个数大于8且当前数组的长度大于64时,此时此索引位置上的所有数据改为红黑树存储。
HashMap底层典型属性的属性说明

DEFAULT_INITIAL_CAPACITY:HashMap的默认容量,16

DEFAULT_LOAD_FACTOR:HashMap的默认加载因子,0.75

threshold:扩容的临界值=容量×填充因子,16×75 => 12

TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树,8

MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64

LinkedHashMap的底层实现原理(了解)
  • LinkedHashMap底层使用的结构与HashMap相同,因为LinkedHashMap继承于HashMap。

  • 区别在于:LinkedHashMap内部提供了Entry,替换HashMap中的Node。

1598428777520

HashMap的源码分析
        Map map = new HashMap();
        for (int i = 0; i < 12; i++) {
            map.put("i-" + i, i);
        }
        map.put("j-", 99);
//源码分析
/*






*/

6.5 TreeMap的使用

  • 向TreeMap中添加key-value,要求key必须时由同一个类创建的对象
  • 因为要照key进行排序:自然排序、定制排序

6.6 使用Properties读取配置文件

//Properties:常用来处理配置文件。key和value都是String类型
public static void main(String[] args)  {
    FileInputStream fis = null;
    try {
        Properties pros = new Properties();

        fis = new FileInputStream("jdbc.properties");
        pros.load(fis);//加载流对应的文件

        String name = pros.getProperty("name");
        String password = pros.getProperty("password");
        
        System.out.println("name = " + name + ", password = " + password);
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if(fis != null){
            try {
                fis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

7 API总结

Collection接口


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

最佳第六六六人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值