The underlying source code analysis of the methods in the Java collection Collection and the Collection implementation class

One: Overview of the Java Collection Framework

集合和数组都是对多个数据进行存储操作的容器,简称 Java 容器(此时的存储主要指的是内存层面的存储,不涉及到持久化存储(持久化存储:硬盘,服务器))

2: Comparison of collections and arrays

1.1数组在存储多个数据方面的特点

> 一旦初始化以后长度就确定了,不可以对长度进行修改

> 定义数组的时候需要指定数组的元素类型,数组定义好以后,数组元素的类型也就确定了

> 也会表现出多态性

1.2 数组的缺点

> 一旦初始化后长度不可修改

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

> 获取数组中实际元素的个数,数组没有现成的属性和方法可以和使用

< 数组存储数据的特点是有序的,可重复的 对于无序的,不可重复的数据 数组则比较弊端

Three: Java collection can be divided into collection and Map system

Collection 体系:单例集合,用来存储一个一个的对象

List接口:存储有序的,可重复的 “动态数组,相较于数组本身静态”
	<List接口下包含 ArrayList,LinkedList,Vector 三个接口实现类
	
Set接口: 存储无序的,不可重复的  “类似于高中讲的集合 ” 无序,确定,互异
  	<Set接口下包含 HashSet,LinkedHashSet,TreeSet 三个实现类

4: Analysis of the three implementation classes under the List interface

package CollectionListUse;

import org.junit.Test;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * @Author:CT
 * @Date:2021/2/5
 * @Description:CollectionListUse
 * @Version 1.0
 */
/*
 一:list接口:存储有序的,可重复的   “动态数组,相较于数组本身静态”
        <  arraylist “作为list接口的主要实现类,线程不安全,执行效率高 底层使用object[] element”
        <  LinkedList “底层使用双向链表进行存储”
        <  vector  “作为list接口的古老实现类,效率低,线程安全”
二:面试题: 三种实现类的异同
同: 三个类都实现了list接口,都是有序的,可重复的

不同:见上

三:arraylist jdk7 当中的源码分析  类似于stringBuilder,stringBuffer

  arraylist list=new arraylist();// 在底层创建一个类型为object 类型的数组 长度为10
  list.add(123);// elementData[0]=new integer(123) 相当于elementData 中添加数据
  .....
  ....
  list.add(111)// 如果此次添加导致elementData的数组的最大长度不够,则会进行扩容。
  默认情况下扩容为原来的1.5倍,再把原来的数据 调用copy() 方法复制到新的数组当中

  结论: 在开发中我们建议使用带参的构造器 去初始化底层创建数组的长度,会避免在中间环节扩容 提高开发效率

  四:arraylist jdk8 中源码变化

    arraylist list=new arraylist();// 在底层创建一个类型为object 类型的数组,并没有初始化数组的长度,节省了内存空间
    后续的add() 操作于JDk7 一致

    小结:JDK7 和JDK8 的两者种初始化ArrayList 类似于单例模式中的 饿汉式和懒汉式两种设计模式


  五:LinkedList源码分析 JDK8的源码分析

    Linked list=new LinkedList(); 内部声明为Node类型的 first 和 Last 属性,默认值为null
    List.add(123); 将123封装到Node中,创建了Node对象

    其中Node 定义为:体现了Linked List 双向链表

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

    6.Vector 的源码分析:JDK7 和 JDK8 当中通过Vector() 构造器创建对象时,底层都要创建了长度为10的数组
    ,在扩容方面,默认扩容为原来是数组的两倍。。



 */

    /*
    List 接口中的抽象方法
   总结; 常用方法
   增: add(Object obj)

   删: remove(int index)

   改: set(int index,Object)

   查: get(int index)

   插入: add(int index, object ele)

   长度: size()

   遍历: ① Iterator 迭代器
         ② 增强for循环
         ③ 普通的循环
     */
public class CollectionList {
    
    

@Test
    public void test(){
    
    
    ArrayList list=new ArrayList();
    list.add(123);
    list.add(456);
    list.add("AA");

    // 1.void add(int index, Object ele): 在Index 指定位置插入ele 元素
    list.add(2,3);
    System.out.println(list);//[123, 456, 3, AA]

    //2. boolean addAll(int index,Collection ele): 从指定Index位置开始插入ele 集合当中所有的元素

    List<Integer> integers = Arrays.asList(1, 2, 3);//可以初始化多个集合中的对象
    list.addAll(2,integers);
    System.out.println(list);//[123, 456, 1, 2, 3, 3, AA]

    //3. Object get(int index): 获取指定index位置上的值
    Object o = list.get(2);
    System.out.println(o);//1

    //4.int IndexOf(Object obj): 返回obj 在集合当中首次出现的问题 注意是首次出现的位置
    int i = list.indexOf(2);
    System.out.println(i);//3

    //5. int Last IndexOf(object obj): 返回Obj当前集合中末次出现的位置

    int i1 = list.lastIndexOf(3);
    System.out.println(i1);//[123, 456, 1, 2, 3, 3, AA]  值为5

    //6. 移除指定index位置的元素,并返回此元素
    Object remove = list.remove(0);
    System.out.println(remove);//123

    //7. Object set(int index,Object ele): 设置指定index位置的元素为ele
    Object set = list.set(2, 23);

    System.out.println(list);//[456, 1, 23, 3, 3, AA]

    //8. list sublist(int fromIndex, int toIndex): 返回从fromIndex到toIndex位置的子集合 左闭右开

    List list1 = list.subList(2, 4);
    System.out.println(list1);//[23, 3]

}

}

Five: Analysis of the source code and method of the three implementation classes under the Set interface

package ColeectionSetUse;

import org.junit.Test;

import java.util.*;

/**
 * @Author:CT
 * @Date:2021/2/6
 * @Description:ColeectionSetUse
 * @Version 1.0
 */
/*
 Set接口:是Collection的子接口

 一:set接口的特点
  < Set接口: 存储无序的,不可重复的  “类似于高中讲的集合 ” 无序,确定,互异
         三个实现类
         HashSet(set接口的主要实现类 线程不安全 可以存储 null值 )

         LinkedHashset(作为HashSet的子类): 使得遍历其内部的数据时,可以按照添加的顺序去遍历

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

 ① set 接口的没有提供额外的方法
 ② set集合不允许包含相同的元素,如果试着把两个相同的元素加入同一个Set集合当中,则添加失败
 ③ set集合 判断两个对象是否相同不是使用 ==运算符,而是根据equals() 方法

二:向set类中添加元素,其所在类必须要重写两个方法 hashCode() 和equals() 方法
    重写的两个方法尽可能保持一致性(相对的对象必须具有相对的散列码”散列码==哈希值“)
    重写两个方法的小技巧(直接调方法,避免手动重写)

 */
public class CollectionSetuse {
    
    
    @Test
    /*
    一:如何理解:

    无序性:不等于随机性。 存储的数据在底层数组中,并非按照数组的索引顺序去做的添加,而是根据数据的哈希值决定的


    不可重复性:往Set 集合当中添加到数据时不可重复的

    二:添加元素的过程:以HashSet为例
    我们向HashSet中添加元素a,首先计算出元素a所在类的hashCode(),计算元素a的哈希值,
    此哈希值接着通过某种算法,算出元素a在底层HashSet当中存放的位置(位置:即为索引位置),判断
    数组在此位置上是否已经存在元素
        如果此位置上没有元素为Null 则直接添加成功 ----> 添加成功情况一
        如果此位置上已经存放了元素b或其他元素
            判断元素a与其他元素的哈希值是否相同
                如果哈希值不同  则添加成功------> 添加成功情况二
                如果哈希值相同
                    则比较元素a调用的equals() 方法比较两个元素的实体内容是否相同
                        如果不同则添加成功 equals() 方法返回值为false------> 添加成功情况三
                        如果相同则添加失败 equals()方法返回值为true

       对于添加成功的两种情况二和三: 元素a与已经在指定位置上的数据以链表的方式存储
       JDK 7:元素a放到数组中指向,指向原来的元素
       Jdk 8:原来的元素在数组中,指向新的元素
       (7上8下)

     */
    public void test(){
    
    
        Set set=new HashSet();
        set.add(456);
        set.add(1230);
        set.add("AAA");
        set.add("CC");
        set.add(new Person("Jerry",12));// 此处为什么要重写equals()方法里面的 hasCode()
        set.add(new Person("Jerry",12));
        Iterator iterator = set.iterator();
       while (iterator.hasNext()){
    
    
            System.out.println(iterator.next());//CC AAA 456 1230

        }


    }
    /*
    关于LinkedHashSet的使用
    ① LinkedHashSet作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,用于指向前一个数据和数
    ② 优点:对于频繁的遍历数组操作,LinkedHashSet效率高于HashSet
     */
    @Test
    public void test2(){
    
    
        Set set=new LinkedHashSet();
        set.add(456);
        set.add(1230);
        set.add("AAA");
        set.add("CC");
        set.add(new Person("Jerry",12));// 此处为什么要重写equals()方法里面的 hasCode()
        set.add(new Person("Jerry",12));
        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
    
    
            System.out.println(iterator.next());//456 1230 AAA person[name="Jerry",age=12]

        }

    }
    /*
    一:TreeSet 类的使用
    ① 向TreeSet当中添加数据时,我们必须确定是同一个类提供的,否则会添加失败
    ② 可以按照对象的添加顺序进行输出
    ③ TreeSet添加数据是按照compareTo 去比较添加数据 他判断数据相同与否是按照return 0 如果返回的是0 则相同
    ④ 所以我们在定制排序的时候可以指定哪些数据能进来
     */
    @Test
    public void test3(){
    
    
    Set set=new TreeSet();
    set.add(123);
    set.add(456);
    set.add(456);//此时会有一个数据添加不成功
        System.out.println(set);

        Set set1=new TreeSet();

        /*
        两种排序方式: 自然排序 定制排序

        自然排序(Comparable)中比较两个对象是否相同的标准:CompareTo()方法
        定制排序(Comparator)
         */

    set1.add(new Person("Jerry",18));//此处会添加失败 原因是添加的对象不统一  java.lang.ClassCastException(报这个错误)
    set1.add(new Person("Jrry",134));
    set1.add(new Person("Jrry",54));

        System.out.println(set1);


    }
    @Test
    /*
    定制排序的使用
     */
    public void test5(){
    
    
        Comparator objectComparator = new Comparator(){
    
    

            @Override
            public int compare(Object o1, Object o2) {
    
    
              if(o1 instanceof Person&& o2 instanceof Person){
    
    
                  Person person=(Person)o1;
                  Person person1=(Person)o2;
                  int i = person.getName().compareTo(person1.getName());
                  if (i!=0){
    
    
                      return i;
                  }else{
    
    
                      return Integer.compare(person.getAge(),person1.getAge());
                  }
              }else{
    
    
                  throw new RuntimeException("输入的数据有误");
              }
            }
        };
        Set set1=new TreeSet(objectComparator);
        set1.add(new Person("Jerry",18));//此处会添加失败 原因是添加的对象不统一  java.lang.ClassCastException(报这个错误)
        set1.add(new Person("Jrry",134));
        set1.add(new Person("Jrry",54));
        System.out.println(set1);

    }
}

to sum up:

  1. When we use the methods under the List interface in the collection, the class created by ourselves must override the equals() method, because when adding elements to the collection, the equals() method in the class will be called for comparison to see if it can be added successfully.

  2. When we use HashSet() and LinkedHashSet() under the Set' interface in the collection, we must rewrite the equals() method and Hashcode() in the class. The reason is that when we add elements to the collection, it will first return according to the HashCode method To find the position where this element is added to the underlying array, and then call the equals() method to compare whether it is the same. If it is different, you can add

  3. Disorder in the collection: It means that the elements in the collection are not stored in the order of the array index, but are stored in accordance with the hash value of the elements

  4. The elements stored in the List interface are ordered and repeatable. The elements stored in the Set interface are disordered and cannot be repeated.

Guess you like

Origin blog.csdn.net/weixin_46351306/article/details/113747032