【Java集合 - Collection】List(ArrayList-Vector-LinkedList) / Set(HashSet-TreeSet) 特点、对比、遍历及运用

———————————————————————————————————————————————————————

预备知识 —— 装箱、拆箱

  1. 集合中的元素都为引用数据类型
  2. 集合只存放元素地址,不存放变量。

		// 基本数据类型对应的包装类
        Integer b = new Integer(5);
        Float f = new Float(1.11);
        Double d = new Double(1.23);
        Character c = new Character('y');
        Short s = new Short((short) 9);
        // 将基本数据类型转换为引用数据类型叫做装箱

        int x = 5;
        // 将引用数据类型转换为基本数据类型叫做拆箱

        test(x);
        // java5 版本之前这条语句会报错

        Integer i = 90;
        // java5 版本之后会进行自动装箱

        int i1 = i;
        // 自动拆箱
        

———————————————————————————————————————————————————————

Java中的集合

  • Java 中主要有两种集合:Collection 接口Map 接口
    其中 Collection 下又包含 ListSet 两个子接口。
    Java 中的集合
  • Collection:将一组对象以集合元素的形式组织到一起,在其子接口中分别实现不同的组织方式。
  • Set: Collection 的子接口,不记录元素的保存顺序 ,且 不允许有重复元素。
  • List: Collection 的子接口,记录元素的保存顺序 ,且 允许有重复元素。

———————————————————————————————————————————————————————

Collection

1. Collection 与 Collections

  • Collections:不属于集合,是集合类的工具类
    高度抽象出来的集合,包含了集合的基本操作和属性。
  • Collection: 集合类的 上级接口(父接口),继承于它的接口主要有 List 和 Set。

*Collections 类中定义了多种集合操作方法,实现了对集合元素的排序、取极值、批量拷贝、集合结构转换、循环移位以及匹配性检查等功能。
*Arrays,不属于集合类,是数据对象的工具类。

2. Collection 中元素的存放特点

  • 集合中只允许存放引用数据类型,并且存的是地址

        Collection collection = new ArrayList(); // 多态
        collection.add("123");
        collection.add(123);
        // 此处的 123 为 Integer 类型
        // 实现了自动装箱
        collection.add(new Student());

        // 默认封装 toString()方法
        System.out.println(collection);
        

3. Collection 中的部分方法

  1. isEmpty();
    判断集合中的元素是否为空。
  2. clear();
    清空集合中的所有元素。
  3. size();
    返回集合中的元素个数。

———————————————————————————————————————————————————————

集合的迭代器:Iterator

1. Iterator

Iterator 接口定义了对 Collection 类型对象中所含元素的遍历等增强处理功能。

  • Iterator 称作迭代器,用于容器元素的遍历。
    迭代器统一了对容器的访问方式,这也是接口解耦的最好体现。

		// 迭代器的创建
		// 所有 Collection 接口的实现类都有一个 iterator() 方法,用以返回一个 Iterator(接口)对象。
        Iterator iterator = collection.iterator(); 
        

2. Iterator 接口的方法

iterator 类似于指针,指向所在元素的左边。

  1. boolean hasNext()
    判断集合中是否存在下一个元素。
  2. Object next()
    返回下一个元素并指向下一个位置。
  3. void remove()
    删除元素。

3. 利用迭代器遍历集合


        //遍历集合中的所有元素
        while (iterator.hasNext()){
            Object next = iterator.next();
            System.out.println(next);
        }
        

———————————————————————————————————————————————————————

List

1. List 的特点

  1. 有序,可以有重复元素。
  2. 和数组类似,List 可以动态增长
  3. 查找元素效率高,相对的插入删除元素效率低,因为会引起其他元素位置改变。
  4. List 容器中的元素都对应一个整数型的序号记录其在容器中的位置,可以根据序号(索引)存取元素

2. List 下的实现类

  • List 接口下有三个实现类:
  1. ArrayList
  2. LinkedList
  3. Vector

        // List 集合是接口,不可以创建对象
        // 创建一个 List
        List list = new ArrayList();
        // list 特点:有序、可重复
        

3. List 中的方法

  • List 继承了父类所有的方法。

		// 向 list 中添加元素
        list.add("123");
        list.add(123);
        list.add(123);
        list.add(123);
        list.add(new Object());

		// list.size();
        // 获取 list 的长度
        list.size();

        // list.get(int index);
        // 获取索引为 index 的元素
        list.get(3);
        

4. List 遍历的两种方式

(1) 迭代器遍历数据
        
        // 迭代器遍历数据
        while (iterator.hasNext()){
            Object next = iterator.next();
            System.out.println(next);
        }
        
(2) for 循环遍历数据
        
        // for循环遍历数据
        for (int i = 0;i < list.size();i++){
            System.out.println(list.get(i));
        }

———————————————————————————————————————————————————————

ArrayList

1. ArrayList 的特点
  • ArrayList 实现的是一个动态数组,允许任何符合规则的元素插入(包括 null),它的规模可变并且能像链表一样被访问。
  • 每个 ArrayList 初始容量为 10,随着容器中元素增加,容器也会增加,同时会进行检查。快溢出时,就会扩容,扩容到原来的 1.5 倍(扩容因子为 1.5),所以如果明确插入元素的数量,最好指定一个初始容量值,避免过多扩容浪费时间,效率。
  • ArrayList 的数据结构:数组
    数组的查询效率比较高
  • 它提供的功能类似 Vector 类但不同步,它是以 Array 方式实现的 List ,允许快速随机存取
  • 特点是读快改慢

		// 创建一个容量为 25 的 ArrayList
        ArrayList<String> arrayList = new ArrayList<>(25);
        
2. ArrayList 的部分方法

        ArrayList<Student> arrayList = new ArrayList();
        // 泛型:指定集合装载同一种类型的元素,提高开发的安全性和操作性

        Student stu1 = new Student("张三","男",20);
        Student stu2 = new Student("李四","男",23);
        Student stu3 = new Student("王五","男",21);

        arrayList.add(stu1);
        arrayList.add(stu2);
        arrayList.add(stu3);

        // 删除学生名称为李四的学生信息
        for (int i = 0;i < arrayList.size();i++){
            if (arrayList.get(i).getName().equals("李四")){
                arrayList.remove(i);
            }
        }

3. ArrayList 的两种遍历方式
(1) 迭代器遍历数据

        // 遍历集合数据
        for (int i = 0;i < arrayList.size();i++){
            System.out.println(arrayList.get(i).getName() + "\t\t"
                    + arrayList.get(i).getSex() + "\t" + arrayList.get(i).getAge());
        }
        
(2) foreach 遍历数据

        // foreach 遍历数据
        for (Student stu:arrayList) {
            System.out.println(stu.toString());
        }

———————————————————————————————————————————————————————

Vector

1. Vector 的特点
  1. Vector 是一种老旧的动态数组。
  2. Vector 是线程同步的。
  3. Vector 效率很低,一般不赞成使用。
  4. 与 ArrayList 相似,可以说 Vector 是线程安全的动态数组。操作与 ArrayList 一样。
2. Vector 的部分方法

        List vect = new Vector();

        Student stu1 = new Student("张三","男",20);
        Student stu2 = new Student("李四","男",23);
        Student stu3 = new Student("王五","男",21);

        vect.add(stu1);
        vect.add(stu2);
        vect.add(stu3);
        // 默认将元素装载到已有元素的后面一个位置
        vect.add(2,new Student("赵六","男",22));
        // 默认将元素装载到指定索引的位置

        vect.remove(2);
        // 删除集合中索引为 2 的元素

        // vect.set(int index, Element e);
        // 根据索引修改指定元素的数据
        vect.set(0,new Student("刘七","男",19));

        // vect.contains(Object o);
        // 判断是否包含此对象
        vect.contains(new Student("徐八","男",18));
 
3. Vector 的遍历

        Iterator iterator = vect.iterator();

        // 迭代器遍历数据
        while (iterator.hasNext()){
            // Object next = iterator.next();
            // 因为没有指定泛型,所以默认是 Object 类型的
            // 想要转成 Student 类型的对象需要进行强制转换
            Student next = (Student)iterator.next();
            System.out.println(next.getName() + "\t\t" + next.getSex() + "\t" + next.getAge());
        }
        

———————————————————————————————————————————————————————

ArrayList 和 Vector 的区别

  1. 线程同步,Vector 线程安全,ArrayList 线程不安全。
  2. 效率问题,Vector 效率低,ArrayList 效率高。
  3. 增长数量,Vector 以 2 倍增长,ArrayList 以 1.5 倍增长(ArrayList 和 Vector 都采用线性连续存储空间,当存储空间不足的时候,ArrayList 默认增加原来的 50%,Vector 默认增加原来的一倍)。
  4. Vector 可以设置 capacityIncrement(capacity:容量,Increment:增加,capacityIncrement:容量增长的参数),而 ArrayList 不可以。

此处转载一篇博客园的文章
ArrayList 和 Vector 扩容

———————————————————————————————————————————————————————

LinkedList

1. LinkedList 的特点
  1. LinkedList 的方法基本与 ArrayList 一致,不同的是底层的数据结构。

  2. LinkedList 的数据结构:链表
    链表的增删改操作效率比较高。

  3. LinkedList 实现一个链表,提供最佳顺序存取,适合插入和移除元素
    由这个类定义的链表也可以像栈或队列一样被使用。

  4. 特点是改快读慢

  5. LinkedList 默认装载 Object 类型。

  6. 底层是一个双向链表(链表中有两个属性,第一个存储数据,第二个存储下一个容器的地址)。

  7. 除了 ArrayList 基本方法,还额外提供了add、get、remove方法在 LinkedList 的首部或尾部。

    /*
    * 子类特有功能,不能多态调用
    *
    * addFirst(E) 添加到链表的开头
    * addLast(E) 添加到链表的结尾
    *
    * E getFirst() 获取链表的开头
    * E getLast() 获取链表的结尾
    *
    * E removeFirst() 移除并返回链表的开头
    * E removeLast() 移除并返回链表的结尾
    */

  8. 非同步,不能随机访问,所有操作按照双重链表的需要执行。

  9. 链表中索引的操作将从开头或结尾遍历列表,以较低的代价在 List 中插入和删除。

2. LinkedList 的遍历

        // 创建一个LinkedList对象
        LinkedList linkedList = new LinkedList();

        linkedList.add(123);
        linkedList.add("12345");
        linkedList.add(new Object());
        linkedList.add(789);

        Iterator iterator = linkedList.iterator();
        while (iterator.hasNext()){
            Object next = iterator.next();
            System.out.println(next);
        }

———————————————————————————————————————————————————————

ArrayList 与 LinkedList 的区别

  • ArrayList 与 LinkedList 的区别
  1. 线程安全
    都是线程不安全。

  2. 底层数据结构
    它们都是线性表。
    ArrayList 基于 Object 数组(顺序存储)。
    LinkedList 基于双向循环链表(链式存储)。

  3. 插入和删除是否受元素位置影响
    ArrayList:插入删除的时间复杂度受元素位置的影响。在某个位置插入元素,第 i 个和第 i 个之后的 (n - 1) 个元素都要执行向后/向前移动一位的操作。
    LinkedList:链表,插入,删除,时间复杂度不受元素位置的影响。近似 O(1),而数组近似 O(n)。

  4. 是否快速随机访问
    ArrayList:实现了 RandomAccess 接口,支持快速随机访问
    LinkedList:不支持。随机访问就是通过元素的序号快速获取元素对象。

  5. 内存空间占用
    ArrayList:列表的结尾会预留一定的容量空间。
    LinkedList:每个元素都需要消耗比 ArrayList 更多的空间。

  6. 性能效率
    ArrayList 在查询和修改元素时性能较高,插入和删除元素时效率较低,
    LinkedList 在插入和删除元素时效率较高,查询和修改元素时效率较低。

———————————————————————————————————————————————————————

Set

1. Set 的特点

  1. 无序,不允许重复(访问集合中的元素只能根据元素本身访问)。
    允许null的存在,但仅有一个。
  2. 检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
  3. Set 集合中的元素不按特定方式排序,只是简单的把对象加入集合中,就像往口袋里放东西。
  4. Set 接口没有提供额外的方法。

*任何可变对象,如果在对集合中元素进行操作时,致 e1.equals(e2) == true

*String 类中重写了 hashCode 和 equals 方法用来比较指向的字符串对象所存储的字符串是否相等。

2. Set 下的实现类

  • Set 接口下有两个实现类:
  1. HashSet
  2. TreeSet

———————————————————————————————————————————————————————

HashSet

1. HashSet 的特点
  1. 不可重复、无序(放进去的顺序和取出来的顺序不一样)。
  2. HashSet 中 允许包含值为 null 的元素,但 最多只能有一个 null 元素。
  3. HashSet 底层实现的是 HashMap
  4. 添加进去的元素存在 HashMap 的 key 中。
  5. HashSet 能够快速定位一个元素
  6. 非同步,如果多个线程同时访问一个 HashSet,其中至少一个线程修改了该 Set,必须保持外部同步。
  7. 按照 Hash 算法来存储集合的元素。存取、查询性能好。

HashSet 中 不允许出现重复元素,不保证集合中元素的顺序。

2. HashSet 的遍历

        HashSet hashSet = new HashSet();

        hashSet.add(123);
        hashSet.add(12345);
        hashSet.add(123);
        hashSet.add("456");
        hashSet.add(123);
        hashSet.add(123);
        hashSet.add(789);

        // 迭代器遍历数据
        Iterator iterator = hashSet.iterator();

        while (iterator.hasNext()){
            Object next = iterator.next();
            System.out.println(next);
        }

/*
* 遍历结果:
* 456
* 789
* 12345
* 123
*/

———————————————————————————————————————————————————————

此处插播一条 —— 泛型

JDK1.5 之前集合中的类型不明确,装入集合的元素都被当作 Object 对待,从而失去自己的类型。所以 JDK1.5 之后引入了泛型——

泛型:指定集合装载同一种类型的元素,提高开发的安全性和操作性。

*从集合中取出时往往需要类型转换,效率低下,易出错。
解决办法:定义集合时,同时定义集合中元素的类型。
好处:增强程序的可读性和稳定性。


public class GenericDemo {
    public static void main(String[] args) {
        User<String> user = new User<String>();
        // 创建对象时所赋予的类型就是自定义泛型的类型
        user.name = "张三";
        System.out.println(user.name);
    }
}

// E 自定义泛型
class User<E>{
    // E - 此集合中保存的元素的类型,
    // 主类中创建对象时赋予了什么类型,那它就是什么类型
    E name;
    public E getName(){
        return  null;
    }
}

———————————————————————————————————————————————————————

TreeSet

1. TreeSet 的特点
  1. 不可重复、无序(放进去的顺序和取出来的顺序不一样)。
  2. 内部可自定义排序,确保元素处于排序状态,支持自然(默认方式)和定制排序。
  3. TreeSet 底层基于 TreeMap 实现,非线程安全
  4. TreeSet 通过一个 TreeMap 存储元素,添加进去的元素存在 TreeMap 的 key 中,而 value 统一使用一个 Object 对象。
  5. 所有 key 必须实现 Comparable 接口,并且所有 key 都应该是同一个类的对象
    实现比较器时,需要将 key 强转成比较器接口的父类 -> (Comparable<? super K>) K
    *如若是自定义引用类型,需实现 Comparable 接口,并重写其中的方法(自定义比较规则),否则会报 ClassCastException 异常(未实现 Comparable 接口,向下转型失败)。
  6. 在将对象元素添加到 TreeSet 集合中时,会自动按照某种比较规则将其插入到有序的对象序列中,以保证 TreeSet 集合元素组成的对象序列时刻按照 “升序” 排列。
  7. 该集合不通过 HashCode 和 equals 函数比较元素,而是 compare 或者 CompareTo 函数来判断元素是否相等。
    compare 函数通过判断两个对象的 ID ,相同 ID 为重复元素,不会被加入到集合,而是新元素会覆盖掉旧元素。

import java.util.TreeSet;

public class TreeSet_Comparable {
    public static void main(String[] args) {
        TreeSet<Student> treeSetStu = new TreeSet<>();

        Student stu1 = new Student(20);
        Student stu2 = new Student(19);
        Student stu3 = new Student(21);
        Student stu4 = new Student(18);

        treeSetStu.add(stu1);
        treeSetStu.add(stu2);
        treeSetStu.add(stu3);
        treeSetStu.add(stu4);
        /*
         * 边存放边排序
         * 在存放第一个 key 时,将它作为根节点
         * 从存放第二个 key 开始,每次存放时都需先与 根节点 进行比较
         * 如果 根节点 小于 传入的 key,往树的左边去找
         * 如果 根节点 大于 传入的 key,往树的右边去找
         * 如果 根节点 等于 传入的 key,将原来的 age 覆盖(元素不可重复)
         */

        System.out.println(treeSetStu);
        // [Student{age=18}, Student{age=19}, Student{age=20}, Student{age=21}]
    }
}

class Student implements Comparable<Student>{
    // 根据二叉树规则(左中右顺序)
    // 实现比较器接口

    int age;

    public Student(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "age=" + age +
                '}';
    }

    // 重写比较器接口的比较方法
    @Override
    public int compareTo(Student student) {
        // 定义比较规则
        return this.age - student.age;
    }
    /*
     * 如果 当前 Student 的 age 小于 传入的 Student 的 age,往树的左边去找
     * 如果 当前 Student 的 age 大于 传入的 Student 的 age,往树的右边去找
     * 如果 当前 Student 的 age 大于 传入的 Student 的 age,将原来的 age 覆盖(元素不可重复)
     */
}
2. TreeSet 的两种遍历方式

import java.util.Iterator;
import java.util.TreeSet;

public class TreeSetTest {
    public static void main(String[] args) {
        // 创建 TreeSet 对象
        TreeSet<String> treeSet= new TreeSet<>();
        // 特点:不可重复、无序(放进去的顺序和取出来的顺序不一样)
        // 内部自定排序

        treeSet.add("abcf");
        treeSet.add("abce");
        treeSet.add("abcx");
        treeSet.add("abcd");
        treeSet.add("abcz");
        treeSet.add("abcy");

		// 迭代器遍历
        Iterator<String> iterator = treeSet.iterator();

        while (iterator.hasNext()){
            String next = iterator.next();
            System.out.println(next);
        }
		
		// foreach 遍历
		for (Integer tree : treeSet) {
        System.out.println(tree);
    	}
    }
}

/*
* 遍历结果:
* abcd
* abce
* abcf
* abcx
* abcy
* abcz
*/

———————————————————————————————————————————————————————
感谢你看到这里!Thanks♪(・ω・)ノ

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值