底层源码分析Java集合Collection中的方法以及Collection实现类的

一: Java集合框架的概述

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

二:集合与数组的比较

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

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

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

> 也会表现出多态性

1.2 数组的缺点

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

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

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

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

三:java集合可以分为 collection 和 Map 体系

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

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

四:List接口下的三个实现类分析

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]

}

}

五:Set接口下三个实现类的源码和方法分析

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

    }
}

总结:

  1. 我们在使用集合当中List接口下的方法时,自己创建的类必须要重写equals()方法,因为向集合当中添加元素时会调用类中的equals()方法进行比较,看是否能添加成功

  2. 当我们使用集合当中Set‘接口下的HashSet()和LinkedHashSet()时,必须重写类中的equals()方法和Hashcode() ,原因是我们向集合当中添加元素时,首先会根据HashCode方法返回的哈希值去寻找此元素添加在底层数组当中的位置,再调用equals()方法比较是否相同,不同则可以添加

  3. 集合当中的无序性:表示的是集合当中的元素不是按照数组角标的顺序存储,而是按照元素的哈希值存放

  4. List接口存放的元素是,有序的,可重复的 。Set接口存放的元素是无序的,不可重复的。

猜你喜欢

转载自blog.csdn.net/weixin_46351306/article/details/113747032