• Java--集合框架详解


    前言

    Java集合框架的知识在Java基础阶段是极其重要的,我平时使用List、Set和Map集合时经常出错,常用方法还记不牢,
    于是就编写这篇博客来完整的学习一下Java集合框架的知识,如有遗漏和错误,欢迎大家指出。
    以下是我整理的大致Java集合框架内容,本篇博客就是围绕以下内容展开

    注:本篇博客是根据B站Java集合框架的视频进行编写,视频原地址:【千锋】最新版 Java集合框架详解 通俗易懂

    下面我们开始正式学习!!!

    1、集合概念

    1.1、概念:对象的容器,定义了对多个对象进行操作的常用方法。可实现数组的功能

    1.2、集合与数组区别:

    • 数组长度固定,集合长度不固定

    • 数组可以存储基本类型和引用类型,集合只能存储引用类型

    1.3、位置:java.util*

    2、Collection接口

    Collection体系集合

    2.1、Collection父接口

    • 特点:代表一组任意类型的对象,无序、无下标、不能重复

    • 方法:

    2.2、Collection的使用

    (1)Demo1保存字符串(重点关注迭代器iterator的使用)

    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Iterator;
    
    public class Demo1 {
        public static void main(String[] args) {
            //创建集合
            Collection collection = new ArrayList();
            //1.添加元素
            collection.add("香蕉");
            collection.add("苹果");
            collection.add("西瓜");
            System.out.println("元素个数:"+collection.size());
            System.out.println(collection);
    
            //2.删除元素
            //collection.remove("香蕉");
            //collection.clear();//清除
            //System.out.println("删除之后:"+collection.size());
    
            //3.遍历元素(重点)
            System.out.println("==========================");
            //方法一:增强for
            for (Object object:collection) {
                System.out.println(object);
            }
            //方法二:使用迭代器(迭代器专门用来遍历集合的一种方式)
            //iterator3个方法
            //hasNext();有没有下一个元素
            //next();l获取下一个元素
            //remove();删除当前元素
            System.out.println("==========================");
            Iterator iterator = collection.iterator();
            while (iterator.hasNext()) {
                String s = (String) iterator.next();
                System.out.println(s);
                // collection.remove(s);//不允许使用collection删除会引发并发修改错误,只能使用以下方法移除
                //iterator.remove();
            }
            System.out.println("元素个数:"+collection.size());
    
    
            //4.判断
            System.out.println(collection.contains("香蕉"));
            System.out.println(collection.isEmpty());
    
    
        }
    }
    

    结果

    (2)Demo2 保存学生信息

    学生类

    //学生类
    public class Student {
        private String name;
        private int age;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public Student() {
        }
    
        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
    }
    
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Iterator;
    
    //  保存学生信息
    public class Demo2 {
        public static void main(String[] args) {
            //新建Collection对象
            Collection collection = new ArrayList();
    
            Student s1 = new Student("张三",18);
            Student s2 = new Student("李四",19);
            Student s3 = new Student("王五",21);
            //1.添加数据
            collection.add(s1);
            collection.add(s2);
            collection.add(s3);
            
            System.out.println("元素个数:"+collection.size());
            System.out.println(collection.toString());
    
            //2.删除
            //collection.remove(s1);
            //System.out.println("删除之后:"+collection.size());
    
            //3.遍历
            //方法一:增强for
            System.out.println("========增强for===========");
            for (Object object:collection) {
                Student s = (Student) object;
                System.out.println(s.toString());
            }
    
            //方法二:使用迭代器(迭代器专门用来遍历集合的一种方式)
            System.out.println("========使用迭代器===========");
            Iterator iterator = collection.iterator();
            while (iterator.hasNext()) {
                Student s =(Student) iterator.next();
            }
            System.out.println(collection.toString());
    
            //4.判断
            System.out.println(collection.contains(s1));
            System.out.println(collection.isEmpty());
    
    
        }
    }
    

    结果

    3、List接口与实现类

    3.1、List子接口

    • 特点:有序,有下标,元素可以重复

    • 方法:

    3.2、List子接口的使用

    (1)Demo3

    public class Demo3 {
        public static void main(String[] args) {
            //先创建一个集合对象
            List list = new ArrayList();
            //1.添加元素
            list.add("唱");
            list.add("跳");
            list.add(0,"打篮球");//下标为0,放在第一位
            System.out.println("元素个数:"+list.size());
            System.out.println(list.toString());
    
            //2.删除元素
    //        list.remove("唱");
    //        list.remove(1);
    //        System.out.println("元素个数:"+list.size());
    //        System.out.println(list.toString());
    
    
            //3.遍历
            //方法一:使用for遍历
            System.out.println("===========for遍历===============");
            for (int i = 0; i < list.size(); i++) {
                System.out.println(list.get(i));
            }
    
            //方法二:使用增强for
            System.out.println("===========增强for===============");
            for (Object o:list) {
                System.out.println(o);
            }
    
            //方法三:使用迭代器
            System.out.println("===========迭代器===============");
            Iterator iterator = list.iterator();
            while (iterator.hasNext()) {
                System.out.println(iterator.next());
            }
            //方法四:使用列表迭代器
            //和迭代器的区别:可以向前或向后遍历,添加、删除、修改元素
            System.out.println("===========列表迭代器===============");
            ListIterator lit = list.listIterator();
    
            System.out.println("===========列表迭代器从前往后===============");
            while (lit.hasNext()) {
                System.out.println(lit.nextIndex()+":"+lit.next());
            }
    
            System.out.println("===========列表迭代器从后往前===============");
            while (lit.hasPrevious()) {
                System.out.println(lit.previousIndex()+":"+lit.previous());
            }
    
    
            System.out.println("==========================");
            //4.判断
            System.out.println(list.contains("rap"));
            System.out.println(list.isEmpty());
    
            //5.获取位置
            System.out.println(list.indexOf(2));
    
        }
    
    }
    

    结果

    (2)Demo4

    public class Demo4 {
        public static void main(String[] args) {
            //先创建一个集合对象
            List list = new ArrayList();
            //1.添加数字数据(自动装箱)
            list.add(17);
            list.add(27);
            list.add(37);
            list.add(47);
            list.add(57);
            System.out.println("元素个数:"+list.size());
            System.out.println(list.toString());
    
            //2.删除
            //注意这里是用脚标删除的,如果要用数字需要转成Object
            list.remove(0);
            //list.remove((Object) 17);
            //list.remove(new Integer(20));
    
            System.out.println("元素个数:"+list.size());
            System.out.println(list.toString());
    
    
            //3.补充方法subList:返回子集合,包含头不包含尾
            List subList = list.subList(1, 3);
            System.out.println(subList.toString());
    
    
        }
    }
    

    结果:

    3.3、List实现类

    • ArrayList(重点):

      • 数组结构实现,查询快,增删慢
      • jdk1.2版本后加入,运行效率快,线程不安全
    • Vector:

      • 数组结构实现,查询快,增删慢
      • jdk1.0版本后加入,运行效率慢,线程安全
    • LinkedList:

      • 链表结构实现,增删快,查询慢

    3.3.1、ArrayList

    /**
     * ArrayList的使用
     * 特点:有序,有下标,可以重复
     * 存储结构:数组,查找遍历速度快,增删慢
     */
    
    public class Demo5 {
        public static void main(String[] args) {
            //创建结合
            ArrayList arrayList = new ArrayList();
    
            //1.添加元素
            Student s1 = new Student("张三",20);
            Student s2 = new Student("李四",23);
            Student s3 = new Student("王五",19);
            arrayList.add(s1);
            arrayList.add(s2);
            arrayList.add(s3);
            System.out.println("元素个数:"+arrayList.size());
            System.out.println(arrayList.toString());
    
            //2.删除元素
    //        arrayList.remove(0);
    //        arrayList.remove(s2);
            arrayList.remove(new Student("ooof",12));//这样删除需要在Student中重写 equals(this == obj) 方法
    
            //3.遍历元素【重点】
            //使用迭代器
            System.out.println("========= 使用迭代器=========");
            Iterator it = arrayList.iterator();
            while (it.hasNext()) {
                Student s =(Student) it.next();
                System.out.println(s.toString());
            }
            //列表迭代器
            System.out.println("========= 列表迭代器=========");
            ListIterator lit = arrayList.listIterator();
            while (lit.hasNext()) {
                Student s =(Student) lit.next();
                System.out.println(s.toString());
            }
    
    
            System.out.println("========= 列表迭代器逆序=========");
    
            while (lit.hasPrevious()) {
                Student s =(Student) lit.previous();
                System.out.println(s.toString());
            }
    
            //4.判断
            System.out.println(arrayList.contains(new Student("王五",19)));
            System.out.println(arrayList.isEmpty());
            //5.查找
            System.out.println(arrayList.indexOf(new Student("王五",19)));
        }
    }
    

    结果

    源码分析

    • 默认容量:DEFAULT_CAPACITY = 10;

      • 注:如果没有向集合中添加任何元素,容量为0,添加一个元素之后,容量为10,每次扩容大小为原来的1.5 倍
    • 存放元素的数组:elementData

    • 实际元素个数:size

    • 添加元素:add()方法

      public boolean add(E e) {
          modCount++;
          add(e, elementData, size);
          return true;
      }
      
      
      
      
         public void ensureCapacity(int minCapacity) {
              if (minCapacity > elementData.length
                  && !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
                       && minCapacity <= DEFAULT_CAPACITY)) {
                  modCount++;
                  grow(minCapacity);
              }
          }
      
      //grow为核心
          private Object[] grow(int minCapacity) {
              int oldCapacity = elementData.length;
              if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                  int newCapacity = ArraysSupport.newLength(oldCapacity,
                          minCapacity - oldCapacity, /* minimum growth */
                          oldCapacity >> 1           /* preferred growth */);
                  return elementData = Arrays.copyOf(elementData, newCapacity);
              } else {
                  return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
              }
          }
      

    3.3.2、Vector

    • 存储结构:数组

    • 创建集合 Vector vector = new Vector<>();

    • 增加(vector.add())、删除(vector.remove)、判断(vector.contains())同上

    • 遍历--枚举器遍历

    Enumeration en = vector.elements();
    while(en.hasMoreElements()){
      String s = (String)en.nextElement();
      sout(s);
    }
    

    3.3.3、LinkedList

    /**
     *LinkedList的使用
     * 存储结构:双向链表
     * 可以重复添加
     */
    public class LinkedListTest1 {
        public static void main(String[] args) {
    
            //创建集合
            LinkedList linkedList = new LinkedList<>();
            //1.添加元素
            Student s1 = new Student("张三",20);
            Student s2 = new Student("李四",23);
            Student s3 = new Student("王五",19);
            linkedList.add(s1);
            linkedList.add(s2);
            linkedList.add(s3);
    
            System.out.println("元素个数:"+linkedList.size());
            System.out.println(linkedList.toString());
    
            //2.删除
    //        linkedList.remove(s1);
    //        System.out.println("删除之后:"+linkedList.size());
    
            //3.遍历
            //for遍历
            System.out.println("========for=====");
            for (int i = 0; i < linkedList.size(); i++) {
                System.out.println(linkedList.get(i));
            }
            System.out.println("========增强for=====");
            for (Object object:linkedList) {
                Student s = (Student) object;
                System.out.println(s.toString());
            }
            //迭代器
            System.out.println("========迭代器=====");
            Iterator it = linkedList.iterator();
            while (it.hasNext()) {
                Student s =(Student) it.next();
                System.out.println(s.toString());
            }
            System.out.println("========列表迭代器=====");
            ListIterator lit = linkedList.listIterator();
            while (lit.hasNext()) {
                Student s =(Student) lit.next();
                System.out.println(s.toString());
            }
    
            //4.判断
            System.out.println(linkedList.contains(s1));
            System.out.println(linkedList.isEmpty());
    
            //5.获取
            System.out.println(linkedList.indexOf(s1));
    
        }
    }
    

    结果

    源码分析

    transient int size = 0;//集合大小
    
     transient Node first;//头结点
     
     transient Node last;//尾节点
     
      public boolean add(E e) {
            linkLast(e);
            return true;
        }
        
       void linkLast(E e) {
            final Node l = last;
            final Node newNode = new Node<>(l, e, null);
            last = newNode;
            if (l == null)
                first = newNode;
            else
                l.next = newNode;
            size++;
            modCount++;
        }
    

    3.4、ArrayList与LinkedList的区别

    • ArrayList:数组,必须开辟连续空间,查询快,增删慢

    • LinkedList:双向链表,无需开辟连续空间,查询慢,增删快

    4、泛型和工具类

    泛型

    • Java泛型是JDK1.5中引入的一个新特性,其本质是參数化类型,把类型作为参数传递
    • 常见形式有泛型类、泛型接口、泛型方法
    • 语法:
    • 好处:
      • (1)提高代码的重用性
      • (2)防止类型转换异常,提高代码的安全性

    泛型类

    /**
     * 泛型类
     * 语法:
     * T表示类型占位符,表示一种引用类型,如果编写多个用逗号隔开
     * */
    public class MyGeneric {
        //使用泛型T
        //1.创建变量,不能new T()因为T数据类型不确定
        T t;
    
        //2.创建一个方法,作为方法的参数
        public void show(T t){
            System.out.println(t);
        }
    
        //3.使用泛型作为方法的返回值
        public T getT(){
            return t;
        }
    }
    
    public class TestGeneric {
        public static void main(String[] args) {
            //使用泛型类创建对象
            //注意:1.泛型只能使用引用类型,2.不同泛型类型对象之间不能相互赋值
    
            MyGeneric myGeneric = new MyGeneric();
            myGeneric.t = "hello";
            myGeneric.show("你好");
            String t1 = myGeneric.getT();
    
    
            MyGeneric myGeneric2 = new MyGeneric();
            myGeneric2.t = 100;
            myGeneric2.show(200);
            Integer t2 = myGeneric2.getT();
    
    
        }
    }
    

    结果:

    泛型接口

    /**
     *泛型接口
     * 语法:接口名
     * 注意:不能使用泛型来创建静态常量
     */
    
    public interface MyInterface {
        String name = "张三";
    
        T server(T t);
    }
    

    泛型接口实现有两种方法:

    1、指定数据类型

    public class MyInterfaceImpl implements MyInterface{
        @Override
        public String server(String t) {
            System.out.println(t);
            return t;
        }
    }
    

    测试

    MyInterfaceImpl impl = new MyInterfaceImpl();
    impl.server("KKKKKKKK");
    

    2、不指定数据类型

    public class MyInterfaceImpl2 implements MyInterface{
        @Override
        public T server(T t) {
            System.out.println(t);
            return t;
        }
    

    测试

    MyInterfaceImpl2 impl2 = new MyInterfaceImpl2();
    impl2.server(1000);
    

    结果:

    泛型方法

    /**
     *泛型方法
     * 语法:返回值类型
     */
    
    
    public class MyGenericMethod {
        //泛型方法
        public  void show(T t){
            System.out.println("泛型方法"+t);
        }
    }
    

    测试

    //泛型方法
    //不需要指定类型
    MyGenericMethod myGenericMethod = new MyGenericMethod();
    myGenericMethod.show("坤坤");
    myGenericMethod.show(200);
    myGenericMethod.show(3.1415);
    

    结果:

    泛型集合

    • 概念:参数化类型、类型安全的集合,强制集合元素的类型必须一致。
    • 特点:
      • 编译时即可检查,而非逐行时抛出异常。
      • 访问时,不必类型转换(拆箱)。
      • 不同泛型之间引用不能相互赋值,泛型不存在多态。
    public class DemoCollect {
        public static void main(String[] args) {
            ArrayList arrayList = new ArrayList();
            arrayList.add("xxx");
            arrayList.add("yyy");
            //指定String类型一下两条数据就不能添加进去
    //        arrayList.add(10);
    //        arrayList.add(20);
    
            for (String str:arrayList) {
                System.out.println(str);
            }
    
    
            ArrayList arrayList2 = new ArrayList();
            Student s1 = new Student("张三",18);
            Student s2 = new Student("李四",19);
            Student s3 = new Student("王五",21);
            arrayList2.add(s1);
            arrayList2.add(s2);
            arrayList2.add(s3);
    
            Iterator iterator = arrayList2.iterator();
            while (iterator.hasNext()) {
                Student s = iterator.next();
                System.out.println(s.toString());
            }
    
    
        }
    }
    

    测试

    5、Set接口与实现类

    5.1、Set子接口

    • 特点:无序,无下标,元素不可重复

    • 方法:全部继承自Collecton中的方法

    5.2、Set子接口的使用

    /**
     * 测试Set接口的使用
     * 特点:无序,无下标,元素不可重复
     */
    
    
    public class Demo1 {
        public static void main(String[] args) {
            //创建集合
            HashSet set = new HashSet<>();
            //1.添加数据
            set.add("唱");
            set.add("跳");
            set.add("rap");
            //set.add("rap");重复添加不进去
            System.out.println("数据个数:"+set.size());
            System.out.println(set.toString());
    
            //2.删除
            set.remove("唱");
            System.out.println(set.toString());
    
            //3.遍历【重点】
            System.out.println("----增强for----");
            for (String str:set) {
                System.out.println(str);
            }
            System.out.println("----迭代器----");
            Iterator iterator = set.iterator();
            while (iterator.hasNext()) {
                String s = iterator.next();
                System.out.println(s);
            }
    
            //4.判断
            System.out.println(set.contains("打篮球"));
            System.out.println(set.isEmpty());
    
            
    
        }
    }
    

    测试:(注意这里添加的顺序与显示的顺序不同,因为无序)

    5.3、Set实现类

    • HashSet【重点】:
      • 基于HashCode实现元素不重复。
      • 当存入元素的哈希码相同时,会调用equalsi进行确认,如结果为true,则拒绝后者存入。
    • TreeSet:
      • 基于排列顺序实现元素不重复。
      • 实现了SortedSet接口,对集合元素自动排序
      • 元素对象的类型必须实现Comparable接口,指定排序规则
      • 通过CompareTo方法确定是否为重复元素

    5.3.1、HashSet

    HashSet的使用

    (1)Demo2

    /***
     * HashSet集合的使用
     * 存储结构:哈希表(数组+链表+红黑树)
     */
    public class Demo2 {
        public static void main(String[] args) {
            //新建集合
            HashSet hashSet = new HashSet<>();
            //1.添加元素
            hashSet.add("张三");
            hashSet.add("李四");
            hashSet.add("王五");
            hashSet.add("赵六");
            System.out.println("数据个数:"+hashSet.size());
            System.out.println(hashSet.toString());
    
            //2.删除
            hashSet.remove("张三");
            System.out.println("删除后数据个数:"+hashSet.size());
            System.out.println(hashSet.toString());
    
            //3.遍历
            System.out.println("----增强for----");
            for (String str:hashSet) {
                System.out.println(str);
            }
            System.out.println("----迭代器----");
            Iterator iterator = hashSet.iterator();
            while (iterator.hasNext()) {
                String s = iterator.next();
                System.out.println(s);
            }
    
            //4.判断
            System.out.println(hashSet.contains("打篮球"));
            System.out.println(hashSet.isEmpty());
    
    
    
        }
    
    }
    

    测试

    (2)Demo3

    person类

    public class Person {
        private String name;
        private int age;
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", 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;
        }
    
        public Person() {
        }
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
    
    
        @Override
        public int hashCode() {
            int n1 = this.name.hashCode();
            int n2 = this.age;
            return n1+n2;
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
    
            if (o == null || getClass() != o.getClass()) return false;
    
            if(o instanceof Person){
            Person person = (Person) o;
            if(this.name.equals(person.getName())&&this.age == person.getAge()){
                return true;
            }
            }
            return false;
        }
    
        
    }
    

    Demo3

    /***
     * HashSet集合的使用
     * 存储结构:哈希表(数组+链表+红黑树)
     * 存储过程:
     * (1)根据hashcode计算保存位置,如果位置为空,则直接保存,如果不为空,执行第二步
     * (2)再执行equals方法,如果equals方法为true,则认为是重复,否则形成链表
     */
    public class Demo3 {
        public static void main(String[] args) {
    
    
        //新建集合
        HashSet persons = new HashSet<>();
        //1.添加元素
        Person p1 = new Person("乔纳森-乔斯达",20);
        Person p2 = new Person("乔瑟夫-乔斯达",19);
        Person p3 = new Person("空条承太郎",16);
    
        persons.add(p1);
        persons.add(p2);
        persons.add(p3);
        persons.add(new Person("空条承太郎",16));//重写hashCode()形成链表,再重写equals()就不能添加进来了
    
        System.out.println("元素个数:"+persons.size());
        System.out.println(persons.toString());
    
        //2.删除
    
    //    persons.remove(p1);
    //    persons.remove( new Person("乔纳森-乔斯达",20));//这时可以删除
    //    System.out.println("删除之后:"+persons.toString());
    
        //3.遍历
            System.out.println("------for----");
            for (Person person:persons) {
                System.out.println(person.toString());
            }
            System.out.println("------迭代器----");
            Iterator iterator = persons.iterator();
            while (iterator.hasNext()) {
                System.out.println(iterator.next());
            }
    
        //4.判断
            System.out.println(persons.contains(p1));
            System.out.println(persons.isEmpty());
    
        }
    }
    

    测试

    5.3.2、TreeSet

    (1)简单使用

    /**
     * TreeSet的使用
     * 存储结构:红黑树
     */
    public class Demo4 {
        public static void main(String[] args) {
            //创建集合
            TreeSet treeSet = new TreeSet<>();
            //1.添加元素
            treeSet.add("xyz");
            treeSet.add("abc");
            treeSet.add("wer");
            treeSet.add("opq");
            System.out.println("元素个数:"+treeSet.size());
            System.out.println(treeSet.toString());
    
            //2.删除
            treeSet.remove("wer");
            System.out.println("删除后元素个数:"+treeSet.size());
    
            //3.遍历
            for (String str:treeSet) {
                System.out.println(str);
            }
    
            //4.判断
            System.out.println(treeSet.contains("opq"));
    
        }
    }
    

    (2)保存Person类的数据

    /**
     * 使用TreeSet保存数据
     * 存储结构:红黑树
     * 要求:元素必须要实现Comparable接口,compareTo方法返回值为0,认为是重复元素
     */
    
    public class Demo5 {
        public static void main(String[] args) {
            //创建集合
            TreeSet persons = new TreeSet<>();
            //1.添加元素
            Person p1 = new Person("7乔纳森-乔斯达",20);
            Person p2 = new Person("5乔瑟夫-乔斯达",19);
            Person p3 = new Person("3东方仗助",16);
            Person p4 = new Person("3东方仗助",17);
    
            //直接添加不能添加进去,因为没有比较规则,即没有实现Comparable接口,需要在Person类中实现
            persons.add(p1);
            persons.add(p2);
            persons.add(p3);
            persons.add(p4);
    
            System.out.println("元素个数:"+persons.size());
            System.out.println(persons.toString());
    
            //2.删除
            persons.remove(p4);
            System.out.println("元素个数:"+persons.size());
    
            //3.遍历
            Iterator iterator = persons.iterator();
            while (iterator.hasNext()) {
                System.out.println(iterator.next());
            }
    
            //4.判断
            System.out.println(persons.contains(p1));
        }
    }
    

    person类没有实现Comparable接口

    person类实现Comparable接口

    public class Person implements Comparable{
        private String name;
        private int age;
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", 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;
        }
    
        public Person() {
        }
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
    
    
        @Override
        public int hashCode() {
            int n1 = this.name.hashCode();
            int n2 = this.age;
            return n1+n2;
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
    
            if (o == null || getClass() != o.getClass()) return false;
    
            if(o instanceof Person){
            Person person = (Person) o;
            if(this.name.equals(person.getName())&&this.age == person.getAge()){
                return true;
            }
            }
            return false;
        }
    
        //先按姓名比再按年龄比
        @Override
        public int compareTo(Person o) {
            int n1 = this.getName().compareTo(o.getName());
            int n2 = this.age - o.getAge();
            return n1==0?n2:n1;
        }
    }
    

    测试

    Comparator接口实现定制比较(不需要元素实现Comparable接口)

    /**
     * 使用TreeSet保存数据
     * 存储结构:红黑树
     * Comparator:实现定制比较(比较器)
     */
    public class Demo6 {
        public static void main(String[] args) {
            //创建集合,并指定比较规则(匿名内部类)
            TreeSet persons = new TreeSet<>(new Comparator() {
                @Override
                //先比较年龄再比较姓名
                public int compare(Person o1, Person o2) {
                    int n1 = o1.getAge() - o2.getAge();
                    int n2 = o1.getName().compareTo(o2.getName());
                    return n1==0?n2:n1;
                }
            });
    
            //1.添加元素
            Person p1 = new Person("7乔纳森-乔斯达",20);
            Person p2 = new Person("5乔瑟夫-乔斯达",19);
            Person p3 = new Person("3东方仗助",16);
            Person p4 = new Person("4东方仗助",16);
    
    
            persons.add(p1);
            persons.add(p2);
            persons.add(p3);
            persons.add(p4);
    
            System.out.println(persons.toString());
    
        }
    
    }
    

    测试

    TreeSet案例

    /**
     * 要求:使用TreeSet集合实现字符串按照长度进行排序
     * Comparator实现定制比较
     *
     */
    public class Demo7 {
        public static void main(String[] args) {
            //创建集合并指定比较规则
            TreeSet treeSet = new TreeSet<>(new Comparator() {
                @Override
                public int compare(String o1, String o2) {
    
                    int n1 = o1.length() - o2.length();
                    int n2 = o1.compareTo(o2);
    
                    return n1==0?n2:n1;
                }
            });
            //添加数据
            treeSet.add("hello");
            treeSet.add("hello,world");
            treeSet.add("dalian");
            treeSet.add("kunkun");
            treeSet.add("ikun");
            treeSet.add("cat");
            treeSet.add("beijing");
    
            System.out.println(treeSet.toString());
    
        }
    }
    

    测试

    6、Map接口与实现类

    Map体系集合

    Map接口的特点:

    1. 用于存储任意键值对(Key-Value)

    2. 键:无序、无下标、不允许重复(唯一)

    3. 值:无序、无下标、允许重复

    6.1、Map父接口与简单使用

    Map父接口

    • 特点:存储一对数据(Key-Value),无序、无下标,键不可重复,值可重复。

    • 方法:

      • V put(K key,V value) //将对象存入到集合中,关联键值。key重复则覆盖原值。
      • Object get(Object key)//根据键获取对应的值。
      • Set keySet() //返回所有key。
      • Collectionvalues()//返回包含所有值的Collection集合。
      • Set>//键值匹配的Set集合。

    Map接口使用

    /**
     * Map接口的使用
     * 特点:(1)存储键值对(2)键不能重复,值可以重复(3)无序
     */
    public class Demo1 {
        public static void main(String[] args) {
            //创建Map集合
    
            Map map = new HashMap<>();
            //1.添加元素
            map.put("tom","汤姆");
            map.put("jack","杰克");
            map.put("rose","露丝");
    
            //map.put("rose","ooo");value会被替换
    
            System.out.println("元素个数:"+map.size());
            System.out.println(map.toString());
    
            //2.删除
    //        map.remove("tom");
    //        System.out.println("删除后元素个数:"+map.size());
    
            //3.遍历
            //3.1使用keySet()
            System.out.println("-------keySet()遍历---------");
            //Set keySet = map.keySet();
            for(String key:map.keySet()){
                System.out.println(key+"---"+map.get(key));
            }
            //3.2使用entrySet()
            System.out.println("-------entrySet()遍历---------");
            //Set> entries = map.entrySet();
            for (Map.Entry entry: map.entrySet()) {
                System.out.println(entry.getKey()+"---"+entry.getValue());
    
            }
    
            //4.判断
            System.out.println(map.containsKey("jack"));
            System.out.println(map.containsValue("杰克"));
    
    
        }
    }
    

    测试

    keySet()与entrySet()

    6.2、Map集合的实现类

    • HashMap【重点】

      • jdk1.2版本,线程不安全,运行效率快
      • 允许使用null作为key或value
    • Hashtable【了解】

      • jdk1.0版本,线程安全,运行效率慢
      • 不允许使用null作为key或value
    • Properties

      • Hashtable的子类
      • 要求key和value都是String
      • 通常用于配置文件的读取
    • TreeMap

      • 实现了SortedMap接口(是Map的子接口),可以对key自动排序

    6.2.1、HashMap

    Student类:

    public class Student {
        private String name;
        private int stuNo;
    
        public Student(String name, int stuNo) {
            this.name = name;
            this.stuNo = stuNo;
        }
    
        public Student() {
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getStuNo() {
            return stuNo;
        }
    
        public void setStuNo(int stuNo) {
            this.stuNo = stuNo;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", stuNo=" + stuNo +
                    '}';
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Student student = (Student) o;
            return stuNo == student.stuNo && Objects.equals(name, student.name);
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(name, stuNo);
        }
    }
    
    /**
     * HashMap集合的使用
     * 存储结构:哈希表(数组+链表+红黑树)
     * 存储过程:
     *  (1)根据hashcode计算保存位置,如果位置为空,则直接保存,如果不为空,执行第二步
     *  (2)再执行equals方法,如果equals方法为true,则认为是重复,否则形成链表
     */
    
    public class Demo2 {
        public static void main(String[] args) {
            //创建集合
            HashMap students = new HashMap<>();
            //1.添加元素
            Student s1 = new Student("张三",503);
            Student s2 = new Student("李四",509);
            Student s3 = new Student("王五",505);
            students.put(s1,"3班");
            students.put(s2,"7班");
            students.put(s3,"8班");
            //students.put(s3,"9班");键重复不能添加进去,但是值会覆盖
            students.put(new Student("张三",503),"3班");
            //会添加进去,new会在堆中新创建一个对象,如果要让它添加不进去,要在Student中重写hashcode和equals方法
    
            System.out.println("元素个数:"+students.size());
            System.out.println(students.toString());
            
            //2.删除
    //        students.remove(s1);
    //        System.out.println("删除之后:"+students.size());
    
            //3.遍历
            
            //keySet()
            System.out.println("-------keySet()遍历---------");
            for (Student key: students.keySet()) {
                System.out.println(key.toString()+"========="+students.get(key));
            }
            System.out.println("-------entrySet()遍历---------");
            for (Map.Entry entry: students.entrySet()) {
                System.out.println(entry.getKey()+"========="+entry.getValue());
            }
            
            //4.判断
            System.out.println(students.containsKey(s1));
            
            
    
    
        }
    }
    

    测试

    6.2.2、HashMap源码分析

    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;       // aka 16  HashMap初始容量大小
    
    static final int MAXIMUM_CAPACITY = 1 << 30;              // HashMap数组的最大容量
     
    static final float DEFAULT_LOAD_FACTOR = 0.75f;           //默认加载因子为0.75(到75%时扩容)
    
    static final int TREEIFY_THRESHOLD = 8;                  //链表长度大于8时,调整成红黑树
     
    static final int UNTREEIFY_THRESHOLD = 6;                //链表长度小于6时,调整成链表
    
    static final int MIN_TREEIFY_CAPACITY = 64;             //链表长度大于8,并且集合元素个数大于等于64时,调整成红黑树
    
    transient Node[] table;                            //哈希表中的数组
    
    transient int size;                                     //元素个数
    

    构造函数

     public HashMap() {
            this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
        }
        
     //刚创建HashMap之后没有添加元素 table=null,size=0  目的是节省空间
    

    put方法(这个源码不太容易理解,大家尝试理解前每个方法的前几行就可以,有能力的可以深入研究)

    //put方法  
    public V put(K key, V value) {
            return putVal(hash(key), key, value, false, true);
        }
    
    //putVal方法
      final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                       boolean evict) {
            Node[] tab; Node p; int n, i;
            if ((tab = table) == null || (n = tab.length) == 0)
                n = (tab = resize()).length;
            if ((p = tab[i = (n - 1) & hash]) == null)
                tab[i] = newNode(hash, key, value, null);
            else {
                Node e; K k;
                if (p.hash == hash &&
                    ((k = p.key) == key || (key != null && key.equals(k))))
                    e = p;
                else if (p instanceof TreeNode)
                    e = ((TreeNode)p).putTreeVal(this, tab, hash, key, value);
                else {
                    for (int binCount = 0; ; ++binCount) {
                        if ((e = p.next) == null) {
                            p.next = newNode(hash, key, value, null);
                            if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                                treeifyBin(tab, hash);
                            break;
                        }
                        if (e.hash == hash &&
                            ((k = e.key) == key || (key != null && key.equals(k))))
                            break;
                        p = e;
                    }
                }
                if (e != null) { // existing mapping for key
                    V oldValue = e.value;
                    if (!onlyIfAbsent || oldValue == null)
                        e.value = value;
                    afterNodeAccess(e);
                    return oldValue;
                }
            }
            ++modCount;
            if (++size > threshold)
                resize();
            afterNodeInsertion(evict);
            return null;
        }
    
    // resize()方法
    final Node[] resize() {
            Node[] oldTab = table;
            int oldCap = (oldTab == null) ? 0 : oldTab.length;
            int oldThr = threshold;
            int newCap, newThr = 0;
            if (oldCap > 0) {
                if (oldCap >= MAXIMUM_CAPACITY) {
                    threshold = Integer.MAX_VALUE;
                    return oldTab;
                }
                else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                         oldCap >= DEFAULT_INITIAL_CAPACITY)
                    newThr = oldThr << 1; // double threshold
            }
            else if (oldThr > 0) // initial capacity was placed in threshold
                newCap = oldThr;
            else {               // zero initial threshold signifies using defaults
                newCap = DEFAULT_INITIAL_CAPACITY;
                newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
            }
            if (newThr == 0) {
                float ft = (float)newCap * loadFactor;
                newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                          (int)ft : Integer.MAX_VALUE);
            }
            threshold = newThr;
            @SuppressWarnings({"rawtypes","unchecked"})
            Node[] newTab = (Node[])new Node[newCap];
            table = newTab;
            if (oldTab != null) {
                for (int j = 0; j < oldCap; ++j) {
                    Node e;
                    if ((e = oldTab[j]) != null) {
                        oldTab[j] = null;
                        if (e.next == null)
                            newTab[e.hash & (newCap - 1)] = e;
                        else if (e instanceof TreeNode)
                            ((TreeNode)e).split(this, newTab, j, oldCap);
                        else { // preserve order
                            Node loHead = null, loTail = null;
                            Node hiHead = null, hiTail = null;
                            Node next;
                            do {
                                next = e.next;
                                if ((e.hash & oldCap) == 0) {
                                    if (loTail == null)
                                        loHead = e;
                                    else
                                        loTail.next = e;
                                    loTail = e;
                                }
                                else {
                                    if (hiTail == null)
                                        hiHead = e;
                                    else
                                        hiTail.next = e;
                                    hiTail = e;
                                }
                            } while ((e = next) != null);
                            if (loTail != null) {
                                loTail.next = null;
                                newTab[j] = loHead;
                            }
                            if (hiTail != null) {
                                hiTail.next = null;
                                newTab[j + oldCap] = hiHead;
                            }
                        }
                    }
                }
            }
            return newTab;
        }
    

    HashMap源码简单总结

    1. HashMap刚创建时,table是null,为了节省空间,当添加第一个元素时,table容量调整为16
    2. 当元素个数大于阈值(16*0.75=12)时,会进行扩容,扩容后大小为原来的2倍,目的是减少调整元素的个数
    3. jdk1.8,当每个链表长度大于8,并且数组元素个数大于等于64时,会调整为红黑树,日的提高执行效率
    4. jdk1.8当链表长度小于6时,调整成链表
    5. jdk1.8以前,链表是头插入,jdk1.8以后是尾插入

    7、Collections工具类

    • 概念:集合工具类,定义了除存取以外的集合常用方法

    • 方法:

    代码实现

    /**
     * Collections工具类的使用
     *
     */
    public class Demo4 {
        public static void main(String[] args) {
            List list = new ArrayList<>();
            list.add(20);
            list.add(77);
            list.add(23);
            list.add(89);
            list.add(12);
    
            //sort排序
            System.out.println("排序前:"+list.toString());
            Collections.sort(list);//升默认序
            System.out.println("排序后:"+list.toString());
    
    
            //binarySearch二分查找, 找到为下标,没找到为负数
            int i = Collections.binarySearch(list, 12);
            System.out.println(i);
    
            //copy复制
            List dest = new ArrayList<>();
            for (int j = 0; j < list.size(); j++) {
                dest.add(0);
            }
            Collections.copy(dest,list);
            System.out.println(dest.toString());
    
    
            //reverse反转
            Collections.reverse(list);
            System.out.println("反转之后:"+list);
    
            //shuffle  打乱
            Collections.shuffle(list);
            System.out.println("打乱之后:"+list);
    
    
            //补充:list 转成数组
            System.out.println("=======list 转成数组========");
            Integer[] integers = list.toArray(new Integer[0]);
            System.out.println(integers.length);
            System.out.println(Arrays.toString(integers));
    
    
            //数组转成集合
            System.out.println("=======数组转成集合========");
            String[] names = {"小明","小红","小王"};
            //集合是一个受限集合,不能添加和删除
            List list1 = Arrays.asList(names);
            System.out.println(list1);
    
            //把基本类型数组转成集合时,需要修改为包装类
            Integer[] nums = {100,200,300,400,500};
            List integerList = Arrays.asList(nums);
            System.out.println(integerList);
    
    
        }
    }
    

    测试

    8、集合总结

    最后,我们再看一下这张图回顾一下Java集合框架的重点和常用知识

    • 集合的概念:
      • 对象的容器,和数组类似,定义了对多个对象进行操作的常用方法。
    • List集合:
      • 有序、有下标、元素可以重复。(ArrayList、LinkedList、Vector)
    • Set集合:
      • 无序、无下标、元素不可重复。(HashSet、TreeSet)
    • Map集合:
      • 存储一对数据,无序、无下标,键不可重复,值可重复。(HashMap、HashTable、TreeMap)
    • Collections:
      • 集合工具类,定义了除了存取以外的集合常用方法。

    到这里关于Java集合框架的知识就结束啦°꒰๑'ꀾ'๑꒱°,希望大家都有所收获,觉得文章还可以的话可以点个推荐支持博主啊


    __EOF__

  • 本文作者: 鹤鸣呦呦、、
  • 本文链接: https://www.cnblogs.com/qjds/p/16552897.html
  • 关于博主: 评论和私信会在第一时间回复。或者直接私信我。
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
  • 声援博主: 如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。
  • 相关阅读:
    并查集_find()_连通块_食物链
    面试官:JVM调优,主要针对是哪一个区域?JVM内存结构是怎样的?
    猿如意|IntelliJ IDEA Community下载安装以及基础开发设置和快捷键设置的详细教程
    深度学习之文本分类 ----FastText
    [BUUCTF NewStar 2023] week5 Crypto/pwn
    设计模式23——状态模式
    redis介绍&命令&性能数据及监控&缓存穿透
    判断sparse matrix是否是对称矩阵
    厉害了!阿里内部都用的Spring+MyBatis源码手册,实战理论两不误
    NAT如何配置地址转换
  • 原文地址:https://www.cnblogs.com/qjds/p/16552897.html