美文网首页
JavaSE进阶八 集合一 Collection接口

JavaSE进阶八 集合一 Collection接口

作者: SimonLike | 来源:发表于2021-06-15 11:22 被阅读0次

    1 集合概述

    • 什么是集合,有什么作用?

      数组其实就是一个集合,集合实际上就是一个容器,可以来容纳其他类型的数据。
      
    • 集合为什么说在开发中使用较多?

        集合是一个容器,是一个载体,可以一次容纳多个对象。在实际开发中,假如链接数据库,
        数据库中有10条记录,那么假设把这10条记录查询出来,在Java程序中会将10条数据封装
        成10个java对象,然后将10个java对象放到某一个集合当中,将集合传到前端,然后遍历
        集合,将一个数据一个数据展现出来。
      
    • 集合不能直接存储基本数据类型,另外集合也不能直接存储java对象,集合当中存储的都是java对象的内存地址。(或者说是集合中存储的是引用。)

    • 在java中每一个不同的集合,底层对应不同的数据结构。往不同的集合中存储元素,等于将数据放到了不同的数据结构当中。 数据结构:数据存储的结构就是数据结构;不同的数据结构,数据存储方式不同。

       例如:数组、二叉树、链表、哈希表、...  都是常见的数据结构
       new ArrayList(); 创建一个集合,底层是数组。
       new LinkedList(); 创建一个集合,底层是链表。
       new TreeSet(); 创建一个集合,底层是二叉树。
      
    • 集合中分为两大类

      • 单个方式存储元素,超级父接口:java.util.Collection;
      • 以键值对的方式存储元素, 超级父接口:java.util.Map;
    • 所有的集合类和集合接口都在java.util包下。

    2 Collection接口

    • Collection中存放什么元素?

      • 没有使用"泛型"之前,Collection中可以存储Object的所有子类型。

      • 使用了"泛型"之后,Collection中只能存储某个具体的类型。

      • 集合后期我们会学习"泛型"语法,目前不用管。(再次强调:集合中不能直接存储基本数据类型,也不能存储java对象,只是存储对象的内存地址。)

    2.1 关于java.util.Collection接口中常用的方法

    •   boolean add(Object e); 向集合中添加元素
        int size(); 获取集合中元素的个数
        void clear(); 清空集合
        boolean contains(Object o); 判断集合中是否包含某个元素;包含是true,不包含是false。
        boolean remove(Object o); 删除集合中某个元素
        boolean isEmpty(); 判断该集合中元素的个数是否为0  为空为零是true,不为空是false。
        Object[] toArray(); 把集合转换成数组。
      
    代码示例
    import java.util.ArrayList;
    import java.util.Collection;
    
    public class CollectionTest01 {
        public static void main(String[] args) {
            // 创建一个集合对象
            // Collection  c = new Collection();//接口是抽象的,无法实例化。
    
            // 多态
            Collection c = new ArrayList();
            // 调用Collection接口中的常用方法
            c.add(120); // 自动装箱
            c.add(3.14);// 自动装箱
            c.add(new Object());
            c.add(false);// 自动装箱
    
            // 获取集合中元素的个数
            System.out.println("集合中元素的个数:" + c.size()); // 4
    
            // 清空集合
            c.clear();
            System.out.println("集合中元素的个数:" + c.size());// 0
    
            // 再向集合中添加元素
            c.add("abc");
            c.add("def");
            c.add("中国人");
            c.add("张三");
    
            // 判断集合中是否包含某个元素
            boolean flag = c.contains("def");
            System.out.println(flag); //true
            System.out.println(c.contains("defh")); //false
    
            System.out.println("集合中元素的个数:" + c.size());// 4
            // 删除集合中某个元素
            c.remove("张三");
            System.out.println("集合中元素的个数:" + c.size());// 3
    
            // 判断集合是否为空(集合中是否存在元素)
            System.out.println(c.isEmpty());// false
            // 清空集合
            c.clear();
            System.out.println(c.isEmpty());// true
    
            // 再向集合中添加元素
            c.add(12);
            c.add("asd");
            c.add("hello");
            c.add("world");
            c.add("美女");
            c.add(new Student());
    
            System.out.println("----------------------------------------------------------");
    
            // 把集合转换成数组。
            Object[] objs = c.toArray();
            // 遍历数组
            for (int i = 0;i < objs.length; i++){
                Object o = objs[i];
                System.out.println(o);
            }
        }
    }
    
    class Student{
    
    }
    

    2.2 关于集合遍历/迭代专题(对象Iterator)

    • 以下讲解的遍历方式/迭代方式,是所有Collection通用的一种方式。

    • 在map集合中不能使用,所有的Collection以及其子类中都可以使用。

    • 以下两个方法是迭代器对象Iterator中的方法:

        boolean hasNext(); 如果仍有原素可以迭代,则返回true。
        Object next(); 返回迭代的下一个元素。
      
    代码示例
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Iterator;
    
    public class CollectionTest02 {
        public static void main(String[] args) {
    
            // 创建集合对象
            Collection c = new ArrayList(); // 有序可重复
    
            // 添加元素
            c.add("abc");
            c.add("def");
            c.add(123);
            c.add(321);
            c.add("def");
            c.add(new Object());
    
            // 对集合进行遍历/迭代
            // 第一步:获取集合对象的迭代器对象Iterator
            Iterator it = c.iterator();
            // 第二步:通过迭代器对象开始迭代/遍历集合
            /*
                以下两个方法是迭代器对象Iterator中的方法:
                    boolean hasNext(); 如果仍有原素可以迭代,则返回true。
                    Object next(); 返回迭代的下一个元素。
             */
            //遍历迭代
            while (it.hasNext()){
                Object obj = it.next();
                System.out.print(obj);
                if (obj instanceof Integer){
                    System.out.println(" 是Integer类型");
                }else if (obj instanceof String){
                    System.out.println(" 是String类型");
                }else if (obj instanceof Object){
                    System.out.println(" 是Object类型");
                }
            }
        }
    }
    

    2.3 关于Collection集合中的contains方法

    • boolean contains(Object o); 判断集合中是否包含某个元素;包含是true,不包含是false。
    • 它在底层是怎么判断集合中是否包含某个元素:调用了equals方法进行比对的。
      • 存放在集合中类型,一定要重写equals方法,不然比较的就是内存地址。
    代码示例
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Objects;
    
    public class CollectionTest03 {
        public static void main(String[] args) {
            // 创建集合对象
            Collection c = new ArrayList();
            // 向集合中存储元素
            String s1 = new String("abc");
            c.add(s1);
    
            String s2 = new String("def");
            c.add(s2);
    
            System.out.println("集合汇总元素个数:" + c.size());
    
            String x = new String("abc");
            System.out.println(c.contains(x));// 等于调用了equals方法进行比对的。true
    
            // ===============================================================================
    
            // 测试contains方法
            Collection c1 = new ArrayList();
            //创建用户对象
            User user1 = new User("java");
            User user2 = new User("java");
    
            //加入集合
            c1.add(user1);
            // 判断集合中是否包含user2
            // 没重写equals方法之前 输出false
            // 重写equals方法之后 输出true
            System.out.println(c1.contains(user2));
    
        }
    }
    
    class User{
        private String name;
    
        public User() {
        }
    
        public User(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        // 重写equals方法 (系统重写)
        // 这个equals方法的比较原理是:只要姓名一样就表示同一个用户
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof User)) return false;
            User user = (User) o;
            return Objects.equals(getName(), user.getName());
        }
    }
    

    2.4 关于Collection集合中的remove方法

    • boolean remove(Object o); 删除集合中某个元素
    • 它在底层是怎么判断删除集合中的哪个元素:调用了equals方法进行比对。
    • 重点:
      • 当集合的结构发生改变是,迭代器必须重新获取,如果还是用老的迭代器,会出现异常:java.util.ConcurrentModificationException
      • 在迭代/遍历集合元素的过程中,不能调用集合对象的remove方法删除元素。不然会出现异常:java.util.ConcurrentModificationException
      • 在迭代元素的过程中,一定要使用迭代器Iterator的remove方法删除元素,不要使用集合自带的remove方法删除元素。
    代码示例
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Iterator;
    
    public class CollectionTest04 {
        public static void main(String[] args) {
    
            // 创建集合
            Collection c = new ArrayList();
            String s1 = new String("java");
            c.add(s1);
            String s2 = new String("java");
            // 删除集合中某个元素
            c.remove(s2);
            System.out.println(c.size()); // 0
            System.out.println("======================================================");
    
            // ======================================================
    
            // 创建集合
            Collection c1 = new ArrayList();
    
            // 注意此时获取的迭代器,指向的是集合中没有元素状态下得迭代器。
            // 一定要注意:集合结构只要发生改变,迭代器必须重新获取。
            // 当集合结构发生了改变,迭代器没有重新获取时,调用next()方法
            // 会报异常:java.util.ConcurrentModificationException
    
            Iterator it = c1.iterator();
    
            // 添加元素
            c1.add(1);
            c1.add(2);
            c1.add(3);
    
            // 重新获取迭代器
            it = c1.iterator();// 注释掉该代码 会报异常:java.util.ConcurrentModificationException
    
            while (it.hasNext()){
                Object obj = it.next();
    
                // 删除某一个元素
                // 删除元素之后,集合发生了变化,应该重新获取,
                // 但是循环下一次时没有重新获取迭代器,所以报异常:java.util.ConcurrentModificationException
                // c1.remove(obj);
    
                // 使用迭代器的remove方法删除
                it.remove();//删除迭代器指向的当前元素
                System.out.println(obj);
            }
    
            System.out.println(c1.size()); //0
        }
    }
    

    2.4 关于Collection集合中的list子接口

    • list接口的常用方法:

      • list集合存储元素特点:有序可重复。
      • 有序:list集合中的元素有下标:从0开始,以1递增。
      • 可重复:存储一个1,还可以再存储1。
    • list是Collection接口的子接口,那么list接口有自己"特色"的方法

        list接口特有的常用方法:
        void add(int index, E element); // 在指定位置添加元素(下标,元素)
        E get(int index); //  根据下标获取元素
        int indexOf(Object o); // 获取指定对象第一次出现的索引
        int lastIndexOf(Object o); // 获取指定对象最后一次出现的索引
        E remove(int index); // 删除指定下标位置的元素
        E set(int index, E element);//修改指定下标位置的元素
      
    代码示例
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    public class CollectionTest05 {
        public static void main(String[] args) {
            // 创建list类型的集合。
            List myList = new ArrayList();
            // 添加元素
            myList.add(1);
            myList.add(2);
            myList.add(3);
            myList.add(4);
            myList.add(3);
    
            // 在指定位置添加元素(下标,元素)
            // 这个方法使用不多,因为对于ArrayList集合来说效率比较低。
            myList.add(1,"kind");
    
            // 迭代
            Iterator it = myList.iterator();
            while (it.hasNext()){
                Object obj = it.next();
                System.out.println(obj);
            }
            System.out.println("======================================================");
    
            // 根据下标获取元素
            Object firstObj = myList.get(0);
            System.out.println(firstObj);
    
            System.out.println("======================================================");
    
            // 通过下标遍历。【list集合特有的方式,set没有】
            for (int i = 0;i < myList.size(); i++){
                Object obj = myList.get(i);
                System.out.println(obj);
            }
    
            System.out.println("======================================================");
    
            // 获取指定对象第一次出现的索引
            System.out.println(myList.indexOf(3)); // 3
            // 获取指定对象最后一次出现的索引
            System.out.println(myList.lastIndexOf(3)); // 5
    
            System.out.println("删除前:" + myList.size());
            // 删除指定下标位置的元素
            myList.remove(3);
            System.out.println("删除后:" + myList.size());
    
            // 修改指定下标位置的元素
            myList.set(2,"abc");
    
            // 通过下标遍历。
            for (int i = 0;i < myList.size(); i++){
                Object obj = myList.get(i);
                System.out.println(obj);
            }
        }
    }
    

    3 ArrayList集合

    3.1 ArrayList的使用

    • 默认初始化容量10

      • 集合底层是一个Object[]数组
      • 构造方法: new ArrayList();
    • ArrayList集合的扩容:

      • 增长到原容量的1.5倍。

      • ArrayList集合就是底层数组,如何优化?

         尽可能少的扩容,因为数组扩容效率比较低,
         建议使用ArrayList集合的时候, 给出预估计好的初始化容量值。
        
    • 数组的优缺点:

      • 优点:检索效率比较高。
      • 确定:随机增删元素效率比较低。
      • 注意:向数组末尾增加元素,效率很高,不受影响。
    • ArrayList集合是非线程安全的,如何将ArrayList集合转换成线程安全的

      • 使用集合工具类:java.util.Collections;
    代码示例
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.HashSet;
    import java.util.List;
    
    public class CollectionTest06 {
        public static void main(String[] args) {
    
            // 默认初始化容量是10
            // 数组的长度10
            List list = new ArrayList();
            list.add(1);
    
            // 集合size()方法获取的是当前集合中元素的个数,不是获取集合的容量。
            System.out.println(list.size());
    
            // 指定初始化容量
            // 数组的长度20
            List list1 = new ArrayList(20);
            System.out.println(list1.size());
    
            System.out.println("======================================================");
    
            //创建一个HashSet集合
            Collection c = new HashSet();
            c.add(2);
            c.add(20);
            c.add(200);
            c.add(2000);
    
            // 通过这个构造方法可以将HashSet集合转换成List集合
            List list2 = new ArrayList(c);
            // 遍历
            for (int i = 0; i < list2.size(); i++){
                System.out.println(list2.get(i));
            }
            // ==============================================================
            List myList = new ArrayList();// 非线程安全的
             // 变成线程安全的
            Collections.synchronizedList(myList);
            myList.add(1);
        }
    }
    

    3.2 补充内容位运算

    public class BinaryTest {
        public static void main(String[] args) {
            // 位运算
            // >> 1 二进制右移一位
            // >> 2 二进制右移二位
            // 10的二进制位是:00001010  【10】
            // 右移一位是:00000101   【5】
            System.out.println(10 >> 1);  // 5
    
            // << 1 二进制左移一位
            // << 2 二进制左移二位
            // 10的二进制位是:00001010  【10】
            // 左移一位是:00010100   【20】
            System.out.println(10 << 1);// 20
    
        }
    }
    

    4 LinkedList集合

    • LinkedList集合是双向链表

    • 链表的优点:

      • 由于链表上的元素在空间存储上内存地址不够连续,所以随机增删元素的时候不会有大量的元素位移, 因此随机增删效率较高。
      • 在开发中遇到随机增删集合中元素的业务比较多少时,建议使用LinkedList。
    • 链表的缺点

      • 不能通过数学表达式计算查找元素的内存地址,每一次查找都是从头节点开始遍历,直到找到为止。
      • 所以LinkedList集合检索/查找的效率较低。
    • ArrayList和LinkedList的区别

      • ArrayList:把检索发挥到极致。(末尾添加元素效率还是很高的)
      • LinkedList:把随机增删发挥到极致。
    代码示例
    import java.util.Iterator;
    import java.util.LinkedList;
    import java.util.List;
    
    public class LinkedListTest01 {
        public static void main(String[] args) {
            // 创建LinkedList集合
            List list = new LinkedList();
            // 添加元素
            list.add(2);
            list.add(33);
            list.add(22);
    
            // 输出集合中元素个数
            System.out.println(list.size());
    
            // 使用迭代器变量集合
            Iterator it = list.iterator();
            while (it.hasNext()){
                System.out.println(it.next());
            }
        }
    }
    

    5 Vector集合

    • 底层是一个数组
    • 初始化容量:10
    • 扩容之后是原容量的2倍。
    • Vector中所有的方法都是线程同步的,都带有synchroized关键字,是线程安全的。(效率较低,使用较少)
    代码示例
    import java.util.Iterator;
    import java.util.List;
    import java.util.Vector;
    
    public class VectorTest01 {
        public static void main(String[] args) {
            // 创建Vector集合
            List list = new Vector();
            // 添加元素
            list.add(12);
            list.add(33);
            list.add(22);
    
            // 输出集合中元素个数
            System.out.println(list.size());
    
            // 使用迭代器变量集合
            Iterator it = list.iterator();
            while (it.hasNext()){
                System.out.println(it.next());
            }
        }
    }
    

    5 泛型

    • JDK5.0之后推出的新特性:泛型
    • 泛型这种语法机制只在程序编译阶段起作用,只是给编译器参考的。(运行阶段泛型没用!)
    • 使用泛型的好处:
      • 1,集合中存储的元素类型统一了。
      • 2,从集合中取出的元素类型是泛型指定的类型,不需要进行大量的"向下转型"!
    • 泛型的缺点
      • 集合中存储的元素缺乏多样性。
    代码示例
    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    public class GenericTest01 {
        public static void main(String[] args) {
    
            // 使用泛型List<Animal>之后,表示List集合中只允许存储Animal类型的数据。
            // 使用泛型来指定集合中存储的数据类型。
            List<Animal> mylist = new ArrayList<Animal>();
            // 指定List集合中只能存储Animal,那么存储String就会编译报错
            // 这样用了泛型之后,集合中元素的数据类型更加统一了。
            // mylist.add("abc");
    
            Cat c = new Cat();
            Bird b = new Bird();
    
            mylist.add(c);
            mylist.add(b);
    
            // 获取迭代器
            // 这个表示迭代的是Animal类型。
            Iterator<Animal> it = mylist.iterator();
            while (it.hasNext()){
                Animal a = it.next();
                a.move();// 调用父类方法
                if (a instanceof Cat){
                    ((Cat) a).cathMouse();//调用子类特有方法
                }else if (a instanceof Bird){
                    ((Bird) a).fly();//调用子类特有方法
                }
            }
        }
    }
    class Animal{
        public void move(){
            System.out.println("动物在移动!");
        }
    }
    class Cat extends Animal{
        public void cathMouse(){
            System.out.println("猫在抓老鼠!");
        }
    }
    class Bird extends Animal{
        public void fly(){
            System.out.println("鸟儿在飞翔!");
        }
    }
    

    5.1 自动类型推断机制(又称钻石表达式)

    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    
    public class GenericTest02 {
        public static void main(String[] args) {
            // ArrayList<这里的类型自动推断,可以不写>(),jdk8之后才有的
            // 自动类型推断,钻石表达式!
            List<String> list = new ArrayList<>();
            list.add("abc");
            list.add("def");
            list.add("xxx");
    
            // list.add(11);// 报错类型不匹配
    
            System.out.println("元素个数:" + list.size());
    
            //迭代器遍历
            Iterator<String> its = list.iterator();
            while (its.hasNext()){
                String s = its.next();
                System.out.println(s);
            }
        }
    }
    

    5.2 自定义泛型

    • <>尖括号中是一个标识符,可以随便写的。
    • java源码中常用的是<E>和<T>
      • E是Element单词首字母。
      • T是Type单词首字母。
    代码示例
    public class GenericTest03{
        public static void main(String[] args) {
            // new对象的时候指定了泛型String类型
            GenerricSelf<String> gt = new GenerricSelf<>();
            gt.doSome("11");
            // gt.doSome(11); // 类型不匹配
    
            GenerricSelf<Integer> gt1 = new GenerricSelf<>();
            // gt1.doSome("11");// 类型不匹配
            gt1.doSome(11);
        }
    }
    // 自定义泛型类
    // E代表标识符可以随便写的
    class GenerricSelf<E>{
        public void doSome(E e){
            System.out.println(e);
        }
    }
    

    6 增强for循环(foreach)

    • jdk5.0之后推出的一个新特性。

    • 语法结构:

        for(元素类型 变量名 : 数组或集合){
               System.out.println(f);
           }
      
    • 增强for循环(foreach)没有下标

    代码示例
    public class ForEachTest01 {
        public static void main(String[] args) {
            // int数组
            int[] arr = {12,32,323,82,55,33,98};
            // 遍历数组(普通for循环)
            for (int i = 0; i < arr.length; i++){
                System.out.println(arr[i]);
            }
    
            System.out.println("==================================================");
    
            // 增强for(foreach)
            // f代表的是数组中的元素
            for (int f : arr){
                System.out.println(f);
            }
    
            System.out.println("==================================================");
    
            // 集合使用foreach
            // 创建list集合
            List<String> strings = new ArrayList<>();
            // 添加元素
            strings.add("hello");
            strings.add("word");
            strings.add("java");
    
            // 使用迭代器遍历
            Iterator<String> it = strings.iterator();
            while (it.hasNext()){
                String s = it.next();
                System.out.println(s);
            }
    
            // 使用普通for循环 ,有下标
            for (int i = 0; i < strings.size(); i++){
                System.out.println(strings.get(i));
            }
    
            // 使用增强for循环 foreach,没有下标
            for (String s : strings) {
                System.out.println(s);
            }
        }
    }
    

    上篇:JavaSE进阶七 异常
    下篇:JavaSE进阶八 集合二 Map接口

    相关文章

      网友评论

          本文标题:JavaSE进阶八 集合一 Collection接口

          本文链接:https://www.haomeiwen.com/subject/euczeltx.html