集合两种体系之一:Collection接口(分为List接口和Set接口,以及Collection遍历方法之一的迭代器Iterator)

集合两大体系之一的Collection接口

  • 前言
  • 一、首先先了解集合和数组
    • (一). 集合与数组存储数据概述
    • (二). 数组存储的特点
    • (三). 数组存储的弊端
    • (四). 集合存储的优点
  • 二、集合两大体系之一的Connection接口
    • (一).Collection子接口之List接口
      • 1.Vetor(不常用)
      • 2.ArrayList(常用)
        • 2.1.ArrayLsit底层源码分析(JDK7与JDK8对比)
      • 3.LinkedList
    • (二).Collection子接口之Set接口
      • 1.HashSet以及他的子接口LinkedHashSet
        • 1.1.HashSet和LinkedHashSet底层简介
        • 1.2.元素的添加过程(以HashSet为例)
      • 2.TreeSet
        • 2.1.TreeSet的两种排序方式(Java比较器)
  • 三.Collection遍历的两种方式(迭代器与foeach循环)
    • (一).使用迭代器Iterator
      • 1.迭代器的说明
        • 1.1.遍历的代码实现:
        • 1.2.图解迭代器的遍历过程
    • (二).foreach循环
      • 1.遍历集合举例:


前言

Collection接口继承树
集合两种体系之一:Collection接口(分为List接口和Set接口,以及Collection遍历方法之一的迭代器Iterator)_第1张图片


一、首先先了解集合和数组

(一). 集合与数组存储数据概述

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

(二). 数组存储的特点

1). 一旦初始化以后,其长度就确定了。
2). 数组一旦定义好,其元素的类型也就确定了。我们也就只能操作指定类型的数据了。
*比如:String[] arr;int[] arr1;Object[] arr2;

(三). 数组存储的弊端

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

(四). 集合存储的优点

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

二、集合两大体系之一的Connection接口

(一).Collection子接口之List接口

List接口:存储序的、可重复的数据。
注意:添加的对象,所在的类要重写equals()方法

1.Vetor(不常用)

Vector:作为List接口的古老实现类;线程安全的,效率低;底层使用Object[] elementData存储.

底层源码分析:jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组。
在扩容方面,默认扩容为原来的数组长度的2倍。

(线程安全,效率低,事先创建,不常用)

2.ArrayList(常用)

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

2.1.ArrayLsit底层源码分析(JDK7与JDK8对比)

ArrayList的源码分析
jdk 7情况下:
ArrayList list = new ArrayList();//底层创建了长度是10的Object[]数组elementData。
list.add(123); //elementData[0] = new Integer(123);

list.add(11); //如果此次的添加导致底层elementData数组容量不够,则扩容。
默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。
结论:建议开发中使用带参的构造器:ArrayList list = new ArrayList(int capacity)
使用带参构造器事先确定数组长度,避免底层的默认自动扩容,提高了效率



jdk 8中ArrayList的变化:
ArrayList list = new ArrayList(); //底层Object[] elementData初始化为{}.并没创建长度为10的数组
list.add(123); //第一次调用add()时,底层才创建了长度10的数组,并将数据123添加到elementData[0]

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

小结:jdk7中的ArrayList的对象的创建类似于单例的饿汉式,而jdk8中的ArrayList的对象
的创建类似于单例的懒汉式,延迟了数组的创建,节省内存。

3.LinkedList

LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储.
LinkedList的源码分析
LinkedList list = new LinkedList(); //内部声明了Node类型的first和last属性,默认值为null
list.add(123); //将123封装到Node中,创建了Node对象。
其中,Node定义为:体现了LinkedList的双向链表的说法

 private static class Node {
        E item;
        Node next;
        Node prev;
        Node(Node prev, E element, Node next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
        }
    }

(二).Collection子接口之Set接口

Set接口:存储无序的、不可重复的数据

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

1.HashSet以及他的子接口LinkedHashSet

1.1.HashSet和LinkedHashSet底层简介

HashSet:作为Set接口的主要实现类;线程不安全的;可以存储null值
HashSet底层:数组+链表的结构。(前提:jdk7)
LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历
在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据。
对于频繁的遍历操作,LinkedHashSet效率高于HashSet.
(LinkedHashSet内部是双向链表进行链接,所有可以按添加顺序遍历)

1.2.元素的添加过程(以HashSet为例)

这段要细品~

我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法返回当前对象的哈希值,计算元素a的哈希值,
此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置,判断数组此位置上是否已经元素:
如果此位置上没其他元素,则元素a添加成功。 —>情况1
如果此位置上其他元素b(或以链表形式存在的多个元素,则比较元素a与元素b的hash值:
如果hash值不相同,则元素a添加成功。—>情况2
如果hash值相同,进而需要调用元素a所在类的equals()方法:
equals()返回true,元素a添加失败
equals()返回false,则元素a添加成功。—>情况3

对于添加成功的情况2和情况3而言:元素a与已经存在指定索引位置上数据以链表的方式存储。
jdk 7 :元素a放到数组中,指向原来的元素。
jdk 8 :原来的元素在数组中,指向元素a

总结:七上八下

2.TreeSet

TreeSet:可以照添加对象的指定属性,进行排序。
1.向TreeSet中添加的数据,要求是相同类的对象。
2.两种排序方式:自然排序(实现Comparable接口) 和 定制排序(Comparator)

2.1.TreeSet的两种排序方式(Java比较器)

Java比较器看浏览另一篇博文:
https://blog.csdn.net/weixin_43730875/article/details/119738238


三.Collection遍历的两种方式(迭代器与foeach循环)

(一).使用迭代器Iterator

1.迭代器的说明

(1).java.utils包下定义的迭代器接口:Iterator
(2).Iterator对象称为迭代器(设计模式的一种),主要用于遍历 Collection 集合中的元素。
(3).GOF给迭代器模式的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。迭代器模式,就是为容器而生。

作用:遍历集合Collectiton元素
如何获取实例:coll.iterator()返回一个迭代器实例

1.1.遍历的代码实现:

Iterator iterator = coll.iterator();//coll为Collection的对象
//hasNext():判断是否还下一个元素
while(iterator.hasNext()){
    //next():①指针下移 ②将下移以后集合位置上的元素返回
    System.out.println(iterator.next());
}

注意:在调用it.next()方法之前必须要调用iterator.hasNext()进行检测。若不调用,且
下一条记录无效,直接调用iterator.next()会抛出NoSuchElementException异常。

1.2.图解迭代器的遍历过程

集合两种体系之一:Collection接口(分为List接口和Set接口,以及Collection遍历方法之一的迭代器Iterator)_第2张图片

(二).foreach循环

for(数组元素的类型 局部变量 : 数组对象)
局部变量是数组对象将值赋值给局部变量,即局部变量=数组对象,修改局部变量不会影响数组对象.

1.遍历集合举例:

@Test
public void test(){
    Collection coll = new ArrayList();
    coll.add(123);
    coll.add(456);
    coll.add(new String("Tom"));
    coll.add(false);
    //for(集合元素的类型 局部变量 : 集合对象)
    //内部仍然调用了迭代器。
    for(Object obj : coll){
        System.out.println(obj);
    }
}
  • 说明
    内部仍然调用了迭代器。

你可能感兴趣的:(笔记,Java_SE,集合,java)