Collection集合

1.集合类介绍

面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式。

  • 集合类的特点

集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。

2.Collection接口

在这里插入图片描述

2.1 Collection接口是最基本

  • Collection接口不提供直接的实现,JavaSDK提供的类都是继承自Collection的“子接口”如List和Set。
  • Collection所代表的是一种规则,它所包含的元素都必须遵循一条或者多条规则。
  • Collection接口为集合提供一些统一的访问接口(泛型接口),覆盖了向集合中添加元素、删除元素以及协助对集合进行遍历访问的相关方法
    在这里插入图片描述
    在这里插入图片描述

2.2 Collection的遍历

  • 使用增强型for遍历
    • 使用增强型for循环进行Collection遍历的一般形式
for(元素类型 循环变量名 : Collection对象){
    对循环变量进行处理
}

增强型 for 循环对数组的遍历一样,循环自动将 Collection 中的每个元素赋值给循环变量,在循环中针对该循环变量进行处理则就保证了对 Collection 中所有的元素进行逐一处理

  • 使用迭代器遍历

    因为Collection中有iterator方法,所以每一个子类集合对象都具备迭代器

Iterator 变量名 = Collection对象.iterator();
while(变量名.hasNext()){
    System.out.println(变量名.next());

3.List接口

3.1 List接口为Collection子接口

  • List所代表的是有序的Collection

  • 它用某种特定的插入顺序来维护元素顺序,同时可以根据元素的整数索引(在列表中的位置,和数组相似,从0开始,到元素个数-1)访问元素,并检索列表中的元素
    在这里插入图片描述

  • List由于列表有序并存在索引,因此除了增强for循环进行遍历外,还可以使用普通的for循环进行遍历

3.2 List的常见实现类

3.2.1 List接口实现类-ArrayList

  • 构造方法:

在这里插入图片描述

  • 特点:
  1. 底层实现:数组(不限制数量添加元素,底层动态扩缩容数组)
  2. 查找快,添加和删除慢(数组根据索引查找时间复杂度O(1),添加和删除ArrayList需要扩缩容)
  3. 创建ArrayList对象, 如果使用无参构造,创建的是空列表, 在添加第一个元素的时候,容量才初始化为10
  4. 存储数据当快溢出时,就会进行扩容操作,ArrayList的默认扩容扩展后数组大小为:原数组长度+(原数组长度>>1)
  5. 可添加重复元素,有序
  6. ArrayList是一个非线程安全的列表
  • 代码实现:
import java.util.ArrayList;

public class ArrayListExample {
    public static void main(String[] args) {
        // 创建ArrayList对象
        ArrayList<String> myList = new ArrayList<>();

        // 添加元素
        myList.add("元素1");
        myList.add("元素2");
        myList.add("元素3");

        // 通过索引访问元素
        String element = myList.get(0);
        System.out.println("第一个元素是:" + element);

        // 修改元素
        myList.set(1, "修改后的元素2");

        // 删除元素
        myList.remove(2);

        // 迭代访问元素
        for (String element : myList) {
            System.out.println(element);
        }

        // 检查列表大小
        int size = myList.size();
        System.out.println("列表大小:" + size);
    }
}

3.2.2 LinkedList

  • LinkedList类位置java.util.包,它是List下面的类。找头和尾非常快,效率高,查询少
    在这里插入图片描述
  • 构造方法
    在这里插入图片描述
  • 特点
    (1)有序,可重复
    (2)底层使用双链表存储,所以查找慢(LinkedList不能随机访问,从开头或结尾遍历列表),添加和删除快
    (3)LinkedList也是非同步的

3.2.3 Vector

  • Vector类位置java.util.Vector
    在这里插入图片描述
  • Interface Enumeration是接口
    在这里插入图片描述
    1.Vector与ArrayList相似,操作几乎一样,但是Vector是同步的。所以说Vector是使用数组实现的线程安全的列表;
    2.Vector在进行默认规则扩容时,新数组的长度=原始数组长度*2,也可以指定扩容长度;
    3.创建对象的时候初始化长度为10。

3.2.4 Stack

  • Stack继承自Vector,实现一个后进先出(先进后出)的堆栈
  • Stack提供5个额外的方法使得Vector得以被当作堆栈使用。
  • 基本的push和pop 方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置
  • Stack刚创建后是空栈

3.2.5 Queue

  • 队列是一种先进先出的数据结构,元素在队列末尾添加,在队列头部删除。Queue接口扩展自Collection,并提供插入、提取、检验等操作
  • 接口Deque,是一个扩展自Queue的双端队列,它支持在两端插入和删除元素,因为LinkedList类实现了Deque接口,所以通常我们可以使用LinkedList来创建一个队列。PriorityQueue类实现了一个优先队列,优先队列中元素被赋予优先级,拥有高优先级的先被删除

4.Set接口

  • Set接口位置java.util.Set
  • 特点:一个不包含重复元素的 collection
  • Set接口方法与Collection方法一致。
  • Set接口中常用的子类:
    • HashSet:底层调用HashMap中的方法,集合元素唯一,不保证迭代顺序,线程不安全(不同步),存取速度快。
    • TreeSet:元素不重复,能按照指定顺序排列。存储的对象必须实现Comparable接口。线程不安全(不同步)。
    • LinkedHashSet: 哈希表和链表实现了Set接口,元素唯一,保证迭代顺序。

4.1 Set的常见实现类

4.1.1 EnumSet

  • EnumSet:是枚举的专用Set。所有的元素都是枚举类型

4.1.2 HashSet

  • 常用方法:
    • boolean add(E e) 将指定的元素添加到此集合(如果尚未存在)。
    • void clear() 从此集合中删除所有元素。
    • boolean contains(Object o) 如果此集合包含指定的元素,则返回 true 。
    • boolean remove(Object o) 如果存在,则从该集合中删除指定的元素。
    • int size() 返回此集合中的元素个数。
  • HashSet特点:
    • HashSet:无序不重复,无索引
    • 默认不重复的是虚地址,要想内容不重复,就重写hashcode和equals方法。
    • 底层是HashMap实现,HashMap底层是由数组+链表+红黑树实现
    • HashSet堪称查询速度最快的集合,因为其内部是以HashCode来实现的。它内部元素的顺序是由哈希码来决定的,所以它不保证set的迭代顺序;特别是它不保证该顺序恒久不变
    • 无索引,无法使用for循环来遍历,可以使用增强for循环和迭代器来循环
    • 造成存泄露的原因:HashSet的remove方法也依赖于哈希值进行待删除节点定位,如果由于集合元素内容被修改而导致hashCode方法的返回值发生变更,那么,remove方法就无法定位到原来的对象,导致删除不成功,从而导致内存泄露。
  • HashSet<一>
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class HashSetTest {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        set.add("111");
        set.add("222");
        set.add("e333");
        set.add("44e4");
        set.add("222");
        //HashSet去重
        System.out.println("HashSet元素的数量为:" + set.size());
        System.out.println("----foreach----");
        //添加顺序与遍历元素顺序不一定一致
        for (String s : set) {
            System.out.println(s);
        }
        System.out.println("----Iterator----");
        Iterator<String> iterable = set.iterator();
        while (iterable.hasNext()) {
            System.out.println(iterable.next());
        }
        System.out.println("----contains----");
        boolean bl = set.contains("e333");
        System.out.println(bl);
        System.out.println("----clear----");
        set.clear();  //清空
        System.out.println("HashSet元素的数量为:" + set.size());
    }
}

  • HashSet<二>

import java.util.HashSet;
import java.util.Set;
public class HashSetTest2 {
    public static void main(String[] args) {
        Set<Person> sets = new HashSet<>();
        Person p1 = new Person("jack", 18);
        Person p2 = new Person("jim", 19);
        Person p3 = new Person("lucy", 20);
        System.out.println(p1 == p2);//false
        //p1==p2比较的是什么?比较的是地址值,
        //equals()比较两个对象是否相等,equals是Object的方法,
        //也就是说任何对象都有这个方法,我们就重写equals来符合业务需求
        System.out.println(p1.equals(p2));//false
        /**HashSet为什么优先选择HashCode比较然后再equals?
         * 因为HashCode计算和比较效率高于equals比较*/
        /**HashSet怎么判断两个Person是不是同一个,
         * 首先判断Person对象的HashCode是否相等,
         * 如果不相等就认为不是同一个对象,
         * 如果Hashcode相等,再次判断equals是否返回true,
         * 如果返回true就认为是同一个对象,返货false就认为不是同一个对象*/
        //重写HashCode和equals规则:
        // equals相等Hashcode相同,HashCode相同equals不一定相等(eg:hashCode相同,equals不等)
        sets.add(p1);
        sets.add(p2);
        sets.add(p3);
        System.out.println("sets当前数量:" + sets.size());
        //修改P3的age
        p3.setAge(22);//删不掉
        sets.remove(p3);
        System.out.println("sets当前数量:" + sets.size());
        //再次添加p3,还能添加成功
        sets.add(p3);
        System.out.println("sets当前数量:" + sets.size());
        for (Person person : sets) {
            System.out.println(person);
        }
    }
}
 
import java.util.Objects;
public class Person {
    private String name;
    private int age;
 
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public int getAge() {
        return age;
    }
 
    public void setAge(int age) {
        this.age = age;
    }
 
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
 
    /**
     * 重写hashcode规则
     *
     * @return
     */
 
    @Override
    public int hashCode() {
        return name.hashCode() + age;
    }
//    @Override
//    public int hashCode() {
//        return Objects.hash(name, age);
//    }
}
  • HashSet<三>

public class Student {
    private String no;
    private String name;
    private int age;
    public Student(String no, String name, int age) {
        this.no = no;
        this.name = name;
        this.age = age;
    }
    public String getNo() {
        return no;
    }
//    public void setNo(String no) {
//        this.no = no;
//    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "no='" + no + '\'' +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    //如果业务上有要求:如果两个学生的学号相同就认为是同一个学生,
    //在业务判断中,用equals判断对象是否相等
    @Override
    public boolean equals(Object o) {
        Student otherStu = (Student) o;
        if (this.getNo() == null || otherStu.getNo() == null) {
            return false;
        }
        return this.getNo().equals(otherStu.getNo());
    }
    //重写HashCode,适应HashSet和HashMap
    // equals相等HashCode必须相同时,HashCOde相同equlas不一定相等
    @Override
    public int hashCode() {
        //只有相同的学号字符串,HashCode也相同
        //不同字符串HashCode也有可能相同,不影响,因为HashSet和HashMap,盘算HashCode相等之后还有判断equals
        return this.getNo().hashCode();
    }
}
 
import java.util.HashSet;
import java.util.Set;
public class StudentHashSet {
    public static void main(String[] args) {
        Student s1 = new Student("001", "jack", 20);
        Student s2 = new Student("002", "tom", 30);
        Student s3 = new Student("003", "bxx", 40);
        //在业务看来s1和s5相等
        Student s5 = new Student("003", "lucy", 40);
        Student s4 = s1;
        //没有业务要求,s1,s2,s3是三个不相等的对象(s1,s2,s3内存地址不相等)
        Set<Student> sets = new HashSet<>();
        //HashSet怎么判断s1,s2,s3是不相等的三个对象?
        //1、判断HashCode是否相等
        //2、如果HashCode相同,就判断equals是否相等
        s1.setAge(80);
        s2.setAge(90);
        s3.setAge(100);
//        s1.setNo("004");//在业务上已经发生改变,s1不再是原来的学生了,再次删除不掉,是符合业务需求的
        System.out.println(s1.hashCode());//没修改年龄前的HashCode:460141958
        System.out.println(s2.hashCode());//没修改年龄前的HashCode:1163157884
        System.out.println(s3.hashCode());//没修改年龄前的HashCode:1956725890
        System.out.println(s4.equals(s1));//true
        sets.add(s1);
        sets.add(s2);
        sets.add(s3);
        sets.add(s4);//去重
        sets.add(s5);
        System.out.println("数量:" + sets.size());
        sets.remove(s1);
        System.out.println("数量:" + sets.size());
        //如果业务上有要求:如果两个学生的学号相同就认为是同一个学生,
    }
}

4.1.3 LinkedHashSet

  • LinkedHashSet继承自HashSet,它主要是用链表实现来扩展HashSet类,HashSet中条目是没有顺序的,但是在LinkedHashSet中元素既可以按照它们插入的顺序排序,也可以按它们最后一次被访问的顺序排序
  • 保持Set中元素的插入顺序或者访问顺序,就使用LinkedHashSet

4.1.4 TreeSet

  • TreeSet特点:无序不重复,但是排序。 线程不安全(不同步)。底层基于TreeMap实现。
  • 使用元素的自然顺序(字典顺序)进行排序:
    • 存储非自定义对象(必须本身已经实现Comparable的接口),默认进行排序。
    • 存储自定义对象(需要实现Comparable接口,重写compareTo方法)进行排序。
    • 接口Comparable
      在这里插入图片描述
  • 使用比较器进行排序:
    可以使用外部比较器Comparator,灵活为类定义多种比较器,此时类本身不需要实现Comparable接口;
    在这里插入图片描述

5.Map接口

  • Map是由一系列键值对组成的集合,提供了key到Value的映射。同时它也没有继承 Collection。
  • Map是一个key对应一个value,所以它不能存在相同的 key 值,当然value值可以相同
  • Map接口提供了重要的针对键、值进行操作的接口方法
    在这里插入图片描述

5.1 HashMap

  • 特点:
    • 1.底层实现1.7之前:数组+链表 1.8以后:数组+链表+红黑树
    • 2.key不允许重复,如果key的值相同,后添加的数据会覆盖之前的数据
    • 3.HashMap是非线程安全的,允许存放null键,null值。
  • 数据结构:
    java1.7 HashMap结构
    在这里插入图片描述
    大方向上,HashMap 里面是一个数组,然后数组中每个元素是一个单向链表。上图中,每个绿色

的实体是嵌套类 Entry 的实例,Entry 包含四个属性:key, value, hash 值和用于单向链表的 next

  1. capacity:当前数组容量,始终保持 2^n,可以扩容,扩容后数组大小为当前的 2 倍。
  2. loadFactor:负载因子,默认为 0.75。
  3. threshold:扩容的阈值,等于 capacity * loadFactor

java1.8 HashMap结构:
Java1.8 对 HashMap 进行了一些修改,最大的不同就是利用了红黑树,所以其由 数组+链表+红黑 树 组成。

根据 Java1.7 HashMap 的介绍,查找的时候,根据 hash 值我们能够快速定位到数组的具体下标,但是之后的话,需要顺着链表一个个比较下去才能找到我们需要的,时间复杂度取决于链表的长度,为 O(n)。为了降低这部分的开销,在 Java1.8 中,当链表中的元素超过了 8 个以后, 会将链表转换为红黑树,在这些位置进行查找的时候可以降低时间复杂度为 O(logN)。
在这里插入图片描述

  • 如何判断key是否相同:第一步计算key的hashcode是否想相同,如果不同,就认为key不相同,如果相同进行第二步判断,判断key的equals是否为true,如果为false就是认为key不相同,如果为true就认为key相同
  • 所以我们重写hashcode和equals的原则,hashcode相同,equals不一定相等,但是equals相等,hashcode必须相同

5.2 HashTable

  • Hashtable,它的操作接口和HashMap相同。
  • HashMap和HashTable的区别在于:
    • Hashtable是线程安全的,而HashMap是非线程安全的
    • Hashtable不允许空的键值对,而HashMap可以
    • Hashtable与HashMap都实现Map接口,但二个类的继承的父类不是同一个
      在这里插入图片描述
  • HashTable底层实现:数组+链表+红黑树

5.3 ConcurrentHashMap

  • 特点:ConcurrentHashMap是线程安全并且高效的HashMap
  • 常用方法:同HashMap
  • 数据结构:JDK8
    数组+链表+红黑树,数组的结构可能是链表,也可能是红黑树,红黑树是为了提高查找效率。采用CAS+Synchronized保证线程安全。CAS表示原子操作,例如:i++不是原子操作。Synchronized:表示锁,多线程能够保证只有一个线程操作。
    在这里插入图片描述
  • ConcurrentHashMap比HashTable效率要高

5.4 LinkedHashMap

  • LinkedHashMap继承自HashMap,它主要是用链表实现来扩展HashMap类,HashMap中条目是没有顺序的,但是在LinkedHashMap中元素既可以按照它们插入的顺序排序,也可以按它们最后一次被访问的顺序排序
  • 保持Map中元素的插入顺序或者访问顺序,就使用

5.1 5.5 TreeMap

  • TreeMap特点: 可以对Map集合中的元素进行排序。
    • 1.TreeMap基于红黑树数据结构的实现
    • 2.键可以使用Comparable或Comparator接口, 重写compareTo方法来排序。
    • 3.自定义的类必须实现接口和重写方法,否则抛异
    • 4.Key值不允许重复,如果重复会把原有的value值覆盖。
  • 使用元素的自然顺序(字典顺序)进行排序:
    • 对象(本身具有比较功能的元素)进行排序。
    • 自定义对象(本身没有比较功能的素)进行排序(要进行比较那就让元素具有比较功能,
      那就要实现Comparable这个接口里compareTo的方法)
  • 使用比较器进行排序:
    • 定义一个类实现Comparator接口,覆盖compare方法,将类对象作为参数传递给TreeSet集合的构造方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值