Collection接口

文章目录

  • 1. Java集合框架概述
  • 2. Collection接口中15个方法的使用
  • 3. Iterator(迭代器)接口
  • 4. Connection子接口一:List
    • 4.1 List的实现类
    • 4.2 源码分析
      • 4.2.1 ArrayList源码分析
      • 4.2.2 LinkedList源码分析
      • 4.2.3 Vector源码分析
    • 4.3 List接口中的常用方法
  • 5. Collection子接口之二:Set接口
    • 5.1 Set的实现类
      • 5.1.1 HashSet
      • 5.1.2 LinkedHashSet
      • 5.1.3 TreeSet

  • Java集合框架概述
  • Collection接口方法
  • Iterator迭代器接口
  • Connection子接口一:List
  • Connection子接口二:Set
  • Map接口
  • Connections工具类

1. Java集合框架概述

一方面,面向对象语言对事物的体现都是以对象的形式,为了方便对多个对象的操作,就要对对象进行存储。另一方面,使用数组存储对象具有一些弊端,而Java集合就像一种容器,可以动态地把多个对象的引用放入容器中。

  • 数组在内存存储方面的特点:
    数组初始化以后,其长度就确定了;
    数组声明的类型,就决定了进行元素初始化时的类型,存储的数据类型单一

  • 数组在存储数据方面的弊端:

  • 数组初始化以后,长度就不可变了,不便于扩展;

  • 数组中提供的属性和方法少,不便于进行添加、删除、插入等操作,且效率不高;同时对于获取数组中存储元素个数的需求,没有现成的属性和方法可用;

  • 数组存储的数据是有序的、可以重复的,对于无序、不可重复的需求,不能满足。存储数据的特点单一

Java集合类可以用于存储数量不多的多个对象,还可以用于保存具有映射关系的关联数组。
Note:

  1. 集合、数组都是对多个数据进行存储操作的结构,简称Java容器(此时的存储,主要指的是内存层面的存储,不涉及到持久化的存储(硬盘等,IO流设计持久化层面的存储))

集合框架:
Collection接口继承树:
图中:实线:继承关系,虚线:实现
Collection接口_第1张图片
Map接口继承数:
Collection接口_第2张图片

/----Collection接口:单列集合,用来存储一个一个的对象
	/----List子接口:存储有序的、可重复的数据--->习惯上称作“动态”数组
		/--实现类--ArrayList、LinkedList、Vector
	/----Set子接口:存储无序的、不可重复的数据--->类比高中数学中的“集合”(无序性、确定性、互异性),通常用set来过滤数据
		/--实现类--HashSet、LinkedHashSet、TreeSet
/-----Map接口:双列集合,用来存储一对(Key-Value)一对的数据 --->类比高中函数:y=f(x),多对一
	/--实现类--HashMap、LinkedHashMap、Treemap、HashTable、Properties

2. Collection接口中15个方法的使用

Collection接口_第3张图片

1. 添加
    add(Object obj)
    addAll(Collection coll)
2.获取有效元素的个数
	int size();
package com.weixinyu;

import org.junit.Test;
import sun.plugin.liveconnect.OriginNotAllowedException;

import java.util.*;

public class CollectionTest {
    @Test
    public void test1(){
        Collection coll = new ArrayList();//不能new collection,只能用它的实现类,此处用ArrayList举例

        //1.add(Object e);将元素e添加到集合coll中
        coll.add("aa");
        coll.add("bb");
        coll.add(123);//自动装箱
        coll.add(new Date());

        //2.size();获取添加的元素的个数
        System.out.println(coll.size());//4

        //3.addAll(Collection coll1);将coll1中的元素添加到当前的集合中
        Collection coll1 = new ArrayList();
        coll1.add("cc");
        coll1.add(456);
        coll.addAll(coll1);
        System.out.println(coll.size());//6
        System.out.println(coll);//[aa, bb, 123, Tue Mar 08 22:12:30 CST 2022, cc, 456]

        //4.isEmpty(Collection coll);判断当前集合是否为空
        System.out.println(coll.isEmpty());//false

        //5.clear();清空集合元素
        coll1.clear();
        System.out.println(coll1.isEmpty());//true

    }

    @Test
    public void test2(){

        Collection coll2 = new ArrayList();

        //6.contains(Object obj);判断当前集合中是否包含obj
        //注意:判断时会调用obj对象所在类的equals()方法,所以需要考虑是否重写,
        例如,string()类重写了,如果自己定义的类也要考虑使用==还是判断变量从而考虑是否需要重写
        //要求:向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals().
        coll2.add("bb");
        coll2.add(123);//自动装箱
        coll2.add(456);//自动装箱
        coll2.add(new String("tom"));
        coll2.add(false);//Boolean类型,包装类
        System.out.println(coll2.contains(new String("tom")));//true

        //7.containsAll(Collection coll1);//判断coll1中的所有元素是否都存在于当前集合中
        Collection coll3 = Arrays.asList(123,456);//多态,Arrays.asList()返回的是List,List又是Collection的一个子接口
        System.out.println(coll2.containsAll(coll3));//true

        //8.remove(Object obj);移除该集合中的obj元素
        coll2.remove("bb");
        System.out.println(coll2);//[123, 456, tom, false]

        //9.removeAll(Collection coll1)//从当前集合中移除coll1中所有的元素(移除交集)
        Collection coll4 = Arrays.asList(123,4567);
        coll2.removeAll(coll4);
        System.out.println(coll2);//[456, tom, false]
    }

    @Test
    public void test3(){
        Collection coll = new ArrayList();
        coll.add(123);//自动装箱
        coll.add(456);//自动装箱
        Collection coll1 = Arrays.asList(123,4567,89);

        //10. retainAll(Collection coll1)//获取当前集合和coll1的交集,结果返回给当前集合
        System.out.println(coll.retainAll(coll1));//true
        System.out.println(coll);//[123]

        //11. equals(Object obj);//判断两个集合是否相等,每个元素作比较,ArrayList()有序
        Collection coll2 = Arrays.asList(123);
        System.out.println(coll.equals(coll2));//true
    }

    @Test
    public void test4(){
        Collection coll = new ArrayList();
        coll.add(123);//自动装箱
        coll.add(456);//自动装箱
        Collection coll1 = Arrays.asList(456,123);

        //11. equals(Object obj);//判断两个集合是否相等,每个元素作比较
        // ArrayList()有序不仅要考虑元素是否一样,也要考虑顺序;HashSet()无序,所以只需要考虑元素是否一样,不用考虑顺序
        System.out.println(coll.equals(coll1));//false

        //12. HashCode();//返回当前对象的hash值
        System.out.println(coll.hashCode());//5230,一个hashcode值,不用在意

        //13.toArray();将集合转换成数组
        Object[] arry = coll.toArray();
        for(int i = 0;i < arry.length; i++){
            System.out.println(arry[i]);//123 456
        }
        //拓展: 数组--->集合,调用Arrays类的静态方法asList();
        List list = Arrays.asList(new String[]{"AA","BB","CC"});//返回值是List,当然也是collection
        System.out.println(list);//[AA, BB, CC]

        List list1 = Arrays.asList(new int[]{123,456});
        System.out.println(list1.size());//1

        List list2 = Arrays.asList(new Integer[]{123,456});
        System.out.println(list2.size());//2
    }
}
//14.iterator(); 集合元素的遍历操作,使用迭代器Iterator接口,Iterator内部的方法:hasNext()和next();
        
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;

import org.junit.Test;
public class Main {
    @Test
    public void collectionTest1(){
    Collection coll = new ArrayList();
    coll.add("aa");
    coll.size();
    }
    @Test
    public void iteratorTest() {
        Collection coll = new ArrayList();

        coll.add("aa");
        coll.add(123);//自动装箱
        coll.add(new Date());
        //遍历集合中的元素方法一:
        //Iterator仅用于遍历集合,本身并不提供承装对象的能力,如果需要创建Iterator对象,则必须有一个被迭代的集合
        Iterator ite = coll.iterator();//调用iterator()方法,用以返回一个实现了Iterator接口的对象
//        System.out.println(ite.next());//输出结果为aa,因为集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前。
//        System.out.println(ite.next());//123
//        System.out.println(ite.next());//输出当前日期
        //遍历集合中的元素方法二:
//        for (int i = 0;i < coll.size();i++){
//            System.out.println(ite.next());
//        }
        //上面两种方法一般不使用
        //遍历集合中的元素方法三:
        while(ite.hasNext()){
            System.out.println(ite.next());
        }
    }
}

Note:

  • Iterator对象称为迭代器(设计模式的一种),主要用于遍历Collection集合中的元素。
  • GOF给迭代器模式的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。迭代器模式就是为容器而生。类似于“检票员”的作用。
  • Collection接口继承了java.lang.Iterable接口,该接口有一个iterator()方法,那么所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象。
  • Iterator仅用于遍历集合,本身并不提供承装对象的能力,如果需要创建Iterator对象,则必须有一个被迭代的集合
  • 集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前。

3. Iterator(迭代器)接口

迭代器的执行原理:

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

迭代器中提供了remove();方法,可以在遍历集合的时候删除集合中的元素(不同于Collection中的remove方法,是两个不同的方法),如下:
Note:如果还未调用next()或在上一次调用next方法后已经调用了remove方法,再调用remove都会报illegalStateException。

@Test
    public void iteratorTest2(){
        Collection coll = new ArrayList();
        coll.add("aa");
        coll.add(123);//自动装箱

        Iterator iter = coll.iterator();
        while(iter.hasNext()){
            Object obj = iter.next();
            if ("aa".equals(obj)){
               iter.remove();//删除aa
            }

        }
        Iterator iter1 = coll.iterator();//回到起点,重新遍历
        while (iter1.hasNext()){
            System.out.println(iter1.next());//123
        }
    }

JDK5.0增加了foreach循环,又叫增强for循环,用来遍历集合和数组

@Test
    public void forEachTest(){
        Collection coll = new ArrayList();
        coll.add("aa");
        coll.add(123);
		//for(集合中元素的类型 局部变量名 : 集合对象),foreach内部仍然调用的是迭代器。
        for (Object obj : coll){
            System.out.println(obj);
        }
        //foreach遍历数组
		int[] num = new int[]{1, 2, 3};
        for(int i : num)
            System.out.println(i);
    }

4. Connection子接口一:List

4.1 List的实现类

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

面试题:ArrayList、LinkedList、Vector三者的异同?
同:三者都是List接口的实现类,存储数据的特点都是有序可重复
异:
ArrayList:是List接口的主要实现类;是线程不安全的,效率高,底层使用Object[] elementData存储数据
LinkedList:对于频繁的插入、删除操作,使用此类比ArrayList效率高;因为它底层使用双向链表
Vector:是List接口的古老实现类;线程安全的,效率低;底层也使用底层Object[] 数组存储数据

4.2 源码分析

4.2.1 ArrayList源码分析

JDK7
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)
JDK8中的变化:
ArrayList list = new ArrayList(); //底层Object[] elementData初始化为{}, 并没有创建长度为10的数组
list.add(123);//第一次调用add()时底层才创建了长度为10的数组,并将123添加到elementData中
//后续的操作和JDK7一样
结:JDK7中的ArrayList的对象的床架类似于单例模式的饿汉式,而JDK8中类似于懒汉式,延迟了数组的创建,节省内存。

4.2.2 LinkedList源码分析

JDK7和JDK8无异

LinkedList list = new LinkedList();//内部声明了Node类型的first和last属性,默认值为null
list.add(123);//将123封装到Node中,创建了Node对象,体现了LinkedList双向链表

4.2.3 Vector源码分析

JDK7和JDK8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组,在扩容方面,默认扩容为原来数组的两倍

4.3 List接口中的常用方法

除了从Collection集合中继承的15个方法之外,List集合里添加了一些根据索引来操作集合元素的方法

void add(int index, Object ele)//在index位置插入元素
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位置的元素,并返回此元素
Object set(int index, Object ele)//设置指定index位置的元素为ele
List subList(int fromIndex, int toIndex)//返回从fromIndex到ToIndex位置的子集合

常用的方法为增(add)、删(remove)、改(set)、查(get)、长度(size)、遍历(Iterator, for循环,增强for循环)
循环的三种方法:

public class Test {

    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add(123);
        list.add("wei");
        //方式一:Iterator迭代器
        Iterator ite = list.iterator();
        while(ite.hasNext()) {
            System.out.println(ite.next());
        }
        System.out.println("***********");
        //方式2:foreach循环
        for (Object obj : list) {
            System.out.println(obj);
        }
        System.out.println("***********");
        //方式3:普通for循环
            for ( int i = 0; i < list.size(); i++){
                System.out.println(list.get(i));
            }
        }
    }

5. Collection子接口之二:Set接口

5.1 Set的实现类

  1. Set概述
  • Set接口是Collection的子接口,set接口没有提供额外的方法
  • Set集合不允许包含相同的元素,如果尝试将两个相同的元素加入同一个Set集合中,则添加操作失败
  • Set判断两个对象是否相同不是使用==运算符,而是根据equals()方法
  • 要求:向Set中添加的数据,其所在类一定要重写hashCode()和equals()方法(一般用自动生成的),并且重写的hashCode()和equals()尽可能保持一致,即“相等的对象必须具有相等的散列码”
/----Collection接口:单列集合,用来存储一个一个的对象
	/----Set子接口:存储无序的、不可重复的数据--->类比高中数学中的“集合”(无序性、确定性、互异性),通常用set来过滤数据
		/--HashSet:作为Set接口的主要实现类,是线程不安全的;可以存储null/--LinkedHashSet:作为HashSet的子类,遍历其内部数据时,可以按照添加的顺序遍历,因此对于频繁的遍历操作,LinkedHashSet效率高于HashSet
		/--TreeSet:底层结构是红黑树,可以按照添加对象的指定属性,进行排序,所以要求存入的值必须是同一类型,不能string、123Person等乱存
  • Set的无序性、不可重复性理解(以HashSet为例):
    无序性不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的。
    不可重复性:保证添加的元素按照equals()判断时,不能返回true。即:相同的元素只能添加一个

5.1.1 HashSet

  • HashSet按Hash算法来存储集合中的元素,因此具有很好的存取、查找、删除性能。
  • 作为Set接口的主要实现类,是线程不安全的;可以存储null值

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

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

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

public static void main(String[] args) {
    Set set = new HashSet();
    set.add(456);
    set.add(123);
    set.add("AA");
    set.add("CC");
    set.add(129);
    Iterator ite = set.iterator();
    while (ite.hasNext()){
        System.out.println(ite.next());
    }
}
//遍历输出的顺序和添加的顺序不一样,但是每次遍历输出的顺序相同。注意:无序性指的是添加元素的时候不是一个挨一个的顺序放的

HashSet底层:数组+链表的存储结构。
Collection接口_第4张图片

5.1.2 LinkedHashSet

LinkedHashSet在作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此处数据的前一个数据和后一个数据(双向链表),对应频繁的遍历操作,LinkedHashSet效率高于HashSet

import java.util.*;

public class Test06 {
    public static void main(String[] args) {
        Set set = new LinkedHashSet();
        set.add(456);
        set.add(123);
        set.add("AA");
        set.add("CC");
        set.add(129);
        Iterator ite = set.iterator();
        while (ite.hasNext()){
            System.out.println(ite.next());
        }
    }
}
//遍历输出的顺序和添加的顺序一样

5.1.3 TreeSet

import java.util.*;

public class Test06 {
/*
    1.向TreeSet中添加的数据,要求是相同类的对象
    2.两种排序方式:自然排序(用Comparable接口)和定制排序(Comparator接口)
    3. 自然排序中,比较两个对象是否相同的标准为:CompareTo(Object obj)返回0,不再是equals()
    4. 定制排序中,比较两个对象是否相同的标准为:compare(Object o1, Object o2)返回0,不再是equals()
*/
    public static void main(String[] args) {
        TreeSet set = new TreeSet();
        //不能添加不同类的对象,异常ClassCastException: java.lang.Integer cannot be cast to java.lang.String
//        set.add(456);
//        set.add(123);
//        set.add("AA");

        set.add(123);
        set.add(129);
        set.add(-3);
        set.add(8);
        Iterator ite = set.iterator();
        while (ite.hasNext()){
            System.out.println(ite.next());
        }
    }
}

你可能感兴趣的:(java,数据结构,算法)