Java集合详解(超详细)

Posted AC_Jobim

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java集合详解(超详细)相关的知识,希望对你有一定的参考价值。

Java集合详解(超详细)

一、集合框架的概述

  1. 集合与数组存储数据概述:

集合、数组都是对多个数据进行存储操作的结构,简称Java容器。 说明:此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储(.txt,.jpg,.avi,数据库中)

  1. 数组存储的特点:

一旦初始化以后,其长度就确定了。
数组一旦定义好,其元素的类型也就确定了。我们也就只能操作指定类型的数据了。

  1. 数组存储的弊端:

1.一旦初始化以后,其长度就不可修改。
2.数组中提供的方法非常限,对于添加、删除、插入数据等操作,非常不便,同时效率不高。
3.获取数组中实际元素的个数的需求,数组没有现成的属性或方法可用
4.数组存储数据的特点:有序、可重复。对于无序、不可重复的需求,不能满足。

  1. 集合的框架结构

Java集合可分为Collection和Map两种体系

  1. Collection接口:单列数据,定义了存取一组对象的方法的集合。Collection接口下还有个Queue接口、Set接口、List接口
    List:元素有序、可重复的集合
    Set:元素无序、不可重复的集
  2. Map接口:双列数据,保存具有映射关系“key-value对”的集合

其中:

  1. Set下有HashSet,LinkedHashSet,TreeSet
  2. List下有ArrayList,Vector,LinkedList
  3. Map下有Hashtable,LinkedHashMap,HashMap,TreeMap
|----Collection接口:单列集合,用来存储一个一个的对象
     |----List接口:存储有序的、可重复的数据。  -->“动态”数组
           |----ArrayList:作为List接口的主要实现类,线程不安全的,效率高;底层采用Object[] elementData数组存储
           |----LinkedList:对于频繁的插入删除操作,使用此类效率比ArrayList效率高底层采用双向链表存储
           |----Vector:作为List的古老实现类,线程安全的,效率低;底层采用Object[]数组存储
           
     |----Set接口:存储无序的、不可重复的数据   -->数学概念上的“集合”
           |----HashSet:作为Set接口主要实现类;线程不安全;可以存null值
           		|----LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加顺序遍历;对于频繁的遍历操作,LinkedHashSet效率高于HashSet.
           |----TreeSet:可以按照添加对象的指定属性,进行排序。


|----Map:双列数据,存储key-value对的数据   ---类似于高中的函数:y = f(x)
     |----HashMap:作为Map的主要实现类;线程不安全的,效率高;存储null的key和value
          |----LinkedHashMap:保证在遍历map元素时,可以照添加的顺序实现遍历。
                    原因:在原的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。对于频繁的遍历操作,此类执行效率高于HashMap。
     |----TreeMap:保证照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或定制排序,底层使用红黑树
     |----Hashtable:作为古老的实现类;线程安全的,效率低;不能存储null的key和value
          |----Properties:常用来处理配置文件。key和value都是String类型

二、Collection接口

  • Collection接口是List、Set和Queue接口的父接口,该接口里定义的方法既可用于操作Set集合,也可用于操作List和 Queue集合。
  • JDK不提供此接口的任何直接实现,而是提供更具体的子接口(如:Set和List)实现。
  • 在JDK 5.0之前,Java集合会丢失容器中所有对象的数据类型,把所有对象都当成 Object类型处理;从JDK 5.0增加了泛型以后,Java集合可以记住容器中对象的数据类型。

(一)Collection接口常用方法

  1. 添加
    add(Object obj)
    addAll(Collection coll)
  2. 获取有效元素个数
    int size()
  3. 清空集合
    void clear()
  4. 是否为空集合
    boolean isEmpty()
  5. 是否包含某个元素
    boolean contains(Object obj):是通过元素的equals方法来判断是否是同一个对象
    boolean containsAll(Collection c):也是调用元素的equals方法来比较的。用两个两个集合的元素逐一比较
  6. 删除
    boolean remove(Object obj):通过元素的equals方法判断是否是要删除的那个元素。只会删除找到的第一个元素
    boolean removeAll(Collection coll):取当前集合的差集
  7. 取两个集合的交集
    boolean retainAll(Collection c):把交集的结果存在当前的集合中,不影响c
  8. 集合是否相等
    boolean equals(Object obj)
  9. 转换成对象数组
    Object [] toArray()
  10. 获取集合对象的哈希值
    hashCode()
  11. 遍历
    iterator():返回迭代器对象,用于集合遍历

代码实例:

@Test
public void test01()
    //1.add(Object e):将元素e添加到集合coll中
    Collection coll = new ArrayList();
    coll.add(123);//自动装箱
    coll.add(new Date());
    Collection coll1 = new ArrayList();
    coll1.add("CC");
    //addAll(Collection coll1):将coll1集合中的元素添加到当前的集合中
    coll.addAll(coll1);
    //2.size():获取添加的元素的个数
    System.out.println(coll.size());//3
    //调用collection1中的toString()方法输出
    System.out.println(coll);//[123, Sun Jan 31 14:37:52 CST 2021, CC]
    //3.clear():清空集合元素
    coll.clear();
    //4.isEmpty():判断当前集合是否为空
    System.out.println(coll.isEmpty());//true

@Test
public void Test02()
    Collection coll = new ArrayList();
    coll.add(123);
    coll.add(new String("Tom"));
    coll.add(new Person("Jerry",20));
    //5.contains(Object obj):判断当前集合中是否包含obj
    //判断时需要调用obj对象所在类的equals()方法
    System.out.println(coll.contains(new String("Tom")));//true
    System.out.println(coll.contains(new Person("Jerry",20)));//false,重写Person类的equals()后为true
    //containsAll(Collection coll1):判断形参coll1中的所有元素是否都存在于当前集合中。
    Collection coll1 = Arrays.asList(1234,new String("Tom"));
    System.out.println(coll.containsAll(coll1));//true
    //6.remove(Object obj):从当前集合中移除obj元素。移除成功返回true,否则返回false
    System.out.println(coll.remove(123));//true
    System.out.println(coll);//[Tom, Personname='Jerry', age=20]
    //removeAll(Collection coll1):从当前集合中移除coll1中所有的元素。(差集)
    coll.removeAll(coll1);
    System.out.println(coll);//[Personname='Jerry', age=20]
    Collection a = new ArrayList();
    a.add(123);
    a.add(456);
    a.add(new Person("Jerry",20));
    a.add(new String("Tom"));
    a.add(false);
    //7.retainAll(Collection coll1):交集:获取当前集合和coll1集合的交集,并返回给当前集合
    Collection b = Arrays.asList(123,456,789);
    System.out.println(a.retainAll(b));//true
    System.out.println(a);//[123, 456]


@Test
public void Test03()
    Collection a = new ArrayList();
    a.add(456);
    a.add(new Person("Jerry",20));
    a.add(new String("Tom"));
    //8.equals(Object obj):要想返回true,需要当前集合和形参集合的元素都相同。
    Collection b = new ArrayList();
    b.add(456);
    b.add(new Person("Jerry",20));
    b.add(new String("Tom"));
    System.out.println(a.equals(b));//true
    //9.hashCode():返回当前对象的哈希值
    System.out.println(a.hashCode());//1350934216
    //10.集合 --->数组:toArray()
    Object[] arr = a.toArray();
    //拓展:数组 --->集合:调用Arrays类的静态方法asList()
    List<Object> objects = Arrays.asList(arr);
    System.out.println(objects);
    //注意两个的区别
    List<int[]> ints = Arrays.asList(new int[]123, 456);
    System.out.println(ints.size());//1,集合将其识别为一个元素
    List<Integer> integers = Arrays.asList(new Integer[]123, 456);
    System.out.println(integers.size());//2
    //11.iterator():返回Iterator接口的实例,用于遍历集合元素。

注意:

使用Collection集合存储对象,要求对象所属的类满足:
向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals()。

(二)Iterator迭代器接口

  1. 概述
  • Iterator对象称为迭代器(设计模式的一种),主要用于遍历 Collection 集合中的元素。
  • 迭代器模式:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。
  • Collection接口继承了java.lang.Iterable接口,该接口有一个iterator()方法,那么所
    有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象。
  • 集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前。
  1. Iterator接口的方法
  2. 遍历的代码实现
Iterator iterator = coll.iterator();//获取迭代器对象
//hasNext():判断是否还下一个元素
while(iterator.hasNext())
    //next():①指针下移 ②将下移以后集合位置上的元素返回
    System.out.println(iterator.next());

  1. iterator中remove()方法的使用

注意:

  • 如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove 方法,再调用remove都会报IllegalStateException。
  • 内部定义了remove(),可以在遍历的时候,删除集合中的元素。此方法不同于集合直接调用remove()

代码实例:

Iterator iterator = coll.iterator();
删除集合中"Tom"
while(iterator.hasNext())
    Object obj = iterator.next();
    if("Tom".equals(obj))
        iterator.remove();
    

(三)JDK 5.0新特性–增强for循环:(foreach循环)

  1. 遍历集合举例
@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);
    

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

三、Collection子接口:List接口

(一)List接口概述

  • 概述:

鉴于Java中数组用来存储数据的局限性,我们通常使用List替代数组
List集合类中元素有序、且可重复,集合中的每个元素都有其对应的顺序索引。
List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
JDK AP中List接口的实现类常用的有:ArrayList、LinkedList和 Vector.

  • List接口框架

      |----Collection接口:单列集合,用来存储一个一个的对象
        |----List接口:存储序的、可重复的数据。  -->“动态”数组,替换原的数组
            |----ArrayList:作为List接口的主要实现类;线程不安全的,效率高;底层使用Object[] elementData存储
            |----LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储
            |----Vector:作为List接口的古老实现类;线程安全的,效率低;底层使用Object[] elementData存储
    

(二)List接口常用方法

  • List除了从 Collection集合继承的方法外,List集合里添加了一些根据索引来操作集合元素的方法。
方法描述
void add(int index, Object ele)在index位置插入ele元素
boolean addAll(int index, Collection eles)从index位置开始将eles中的所有元素添加进来
Object get(int index)获取指定index位置的元素
int indexOf(Object obj)返回obj在集合中首次出现的位置
int lastIndexOf(Object obj)返回obj在当前集合中末次出现的位置
Object remove(int index)移除指定index位置(0是第一个元素)的元素,并返回此元素
Object set(int index, Object ele)设置指定index位置的元素为ele
List subList(int fromIndex, int toIndex)返回从fromIndex到toIndex位置的子集合
  • 代码实例:
@Test
public void Test1()
    ArrayList list = new ArrayList();
    list.add(123);
    list.add("AA");
    list.add(new Person("Tom",12));
    System.out.println(list);//[123, AA, Personname='Tom', age=12]
    //1.void add(int index, Object ele):在index位置插入ele元素
    list.add(1,"BB");
    System.out.println(list);//[123, BB, AA, Personname='Tom', age=12]

    //2.boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
    List list1 = Arrays.asList(1,2,3);
    list.addAll(list1);
    System.out.println(list.size());//7

    //3.Object get(int index):获取指定index位置的元素
    System.out.println(list.get(1));//BB

    //4.int indexOf(Object obj):返回obj在集合中首次出现的位置
    System.out.println(list.indexOf("AA"));//3
    System.out.println(list.indexOf("AAC"));//-1,不存在返回-1

    //5.int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
    System.out.println(list.lastIndexOf("BB"));//1

    //6.Object remove(int index):移除指定index位置的元素,并返回此元素
    System.out.println(list);//[123, BB, AA, Personname='Tom', age=12, 1, 2, 3]
    System.out.println(list.remove(0));//123
    System.out.println(list);//[BB, AA, Personname='Tom', age=12, 1, 2, 3]

    //7.Object set(int index, Object ele):设置指定index位置的元素为ele
    list.set(1,"CC");
    System.out.println(list);//[BB, CC, Personname='Tom', age=12, 1, 2, 3]

    //8.List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的左闭右开区间的子集合
    List subList = list.subList(2,4);
    System.out.println(subList);//[Personname='Tom', age=12, 1]

  • 遍历的三种方式
@Test
public void test3()
    ArrayList list = new ArrayList();
    list.add(123);
    list.add(456);
    list.add("AA");
    //方式一:Iterator迭代器方式
    Iterator iterator = list.iterator();
    while(iterator.hasNext())
        System.out.println(iterator.next());
    
    //方式二:增强for循环
    for(Object obj : list)
        System.out.println(obj);
    
    //方式三:普通for循环
    for(int i = 0;i < list.size();i++)
        System.out.println(list.get(i));
    

(三)实现类之一:ArrayList


ArrayList的三个构造方法:

(1)ArrayList()构造一个初始容量为 10 的空列表。

List<String> list1 = new ArrayList<>();

(2)ArrayList(int initialCapacity)构造一个具有指定初始容量的空列表。

List<String> list2 = new ArrayList<>(6);

(3)ArrayList(Collection<? extends E> c)构造一个包含指定 collection 的元素的列表,这些元素是按照该 collection 的迭代器返回它们的顺序排列的。

List<String> list3 = new ArrayList<>(list2);
  • ArrayList是List接口的典型实现类、主要实现类
  • 本质上,ArrayList是对象引用的一个”变长”数组
  • Array Listi的JDK 1.8之前与之后的实现区别?
    JDK 1.7:ArrayList像饿汉式,直接创建一个初始容量为10的数组
    JDK 1.8:ArrayList像懒汉式,一开始创建一个长度为0的数组,当添加第一个元素时再创建一个始容量为10的数组
  • Arrays.asList(…)方法返回的List集合,既不是 ArrayList实例,也不是Vector实例。Arrays.asList(…)返回值是一个固定长度的List集合

(四)实现类之一:LinkedList

  • 对与对于频繁的插入和删除元素操作,建议使用LinkedList类,效率更高
  • 新增方法:
    void addFirst(Object obj),在链表头部插入一个元素
    void addLast(Object obj),在链表尾部添加一个元素
    Object getFirst(),获取第一个元素
    Object getlast)(),获取最后一个元素
    Object removeFirst(),删除头,获取元素并删除
    Object removeLast()删除尾
  • Linkedlist:双向链表,内部没有声明数组,而是定义了Node类型的frst和last,用于记录首末元素。同时,定义内部类Node,作为 Linkedlist中保存数据的基本结构。Node除了保存数据,还定义了两个变量:
    prev变量记录前一个元素的位置
    next变量记录下一个元素的位置
  • LinkedList 是非线程安全的,并发环境下,多个线程同时操作 LinkedList,会引发不可预知的错误

(五)实现类之一:Vector

  • Vector是一个古老的集合,JDK 1.0就有了。大多数操作与ArrayList相同,区别在于Vector是线程安全的
  • 在各种list中,最好把ArrayList作为缺省选择。当插入、删除频繁时,使用LinkedList;Vector总是比ArrayList慢,所以尽量避免选择使用。
  • JDK 7.0和JDK 8.0中通过Vector()构造器创建对象时,底层都创建了长度为10的数组。
  • 在扩容方面,默认扩容为原来的数组长度的2倍。

四、Collection子接口:Set接口

(一)概述

  1. 概述

Set接口是Collection的子接口,set接口没有提供额外的方法
Set集合不允许包含相同的元素,如果试把两个相同的元素加入同一个Set集合中,则添加操作失败。(多用于过滤操作,去掉重复数据)
Set判断两个对象是否相同不是使用==运算符,而是根据equals()方法

  1. 常用类

     |----Collection接口:单列集合,用来存储一个一个的对象
           |----Set接口:存储无序的、不可重复的数据   -->高中讲的“集合”
                |----HashSet:作为Set接口的主要实现类;线程不安全的;可以存储null值
                     |----LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历,对于频繁的遍历操作,LinkedHashSet效率高于HashSet.
                |----TreeSet:可以按照添加对象的指定属性,进行排序。
    

HashSet使用哈希表实现的,元素是无序的。添加、删除操作时间复杂度都是O(1)。


TreeSet内部结构是一个树结构(红黑树),元素是有序的,添加、删除操作时间复杂度为O(log(n)),并且提供了first(), last(), headSet(), tailSet()等方法来处理有序集合。


LinkedHashSet是介于HashSet 和 TreeSet之间,内部是一个双向链表结构,所以它的插入是有序的,时间复杂度是O(1)。

  1. 存储的数据特点:
    用于存放无序的、不可重复的元素

以HashSet为例说明:

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

(二)Set接口常用方法

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

(三)实现类之一:HashSet

概述:

  • Hashset是Set接口的典型实现,大多数时候使用Set集合时都使用这个实现类。
  • HashSet按Hash算法来存储集合中的元素,因此具有很好的存取、查找、删除性能。
  • HashSet具有以下特点:
  1. 不能保证元素的排列顺序
  2. HashSet不是线程安全的
  3. 集合元素可以是nul