Java集合(容器)

集合

集合与数组一样,是一种容器。下面做个集合与数组的对比:

对比项数组集合
容量容量不可变容量可变
可存类型基本数据类型 & 引用数据类型引用数据类型
类型唯一只能存放同一种类型可存放不同类型(一般还是存放同一类型)

Collection 接口

  • Collection 常用方法
    创建Collection对象需要通过多态的形式,其根据 new 的实现类不同体现不同的特性:
    List接口下实现类:[有序]、有下标、可重复
    Set接口下实现类:[无序]、无下标、不可重复

    常用方法名方法描述
    boolean add(Object o)添加一个对象到集合
    boolean remove(Object o)删除指定对象
    void clear()删除集合所有元素
    boolean contains(Object o)查询集合是否有 o 对象
    int size()返回此集合的元素个数
    boolean isEmpty()判断集合是否为空
    boolean addAll(Collection c)将一个集合的所有对象添加到本集合(可自己添加自己)
    boolean equals(Object o)比较两个集合中元素是否相同
    Object[ ] toArray()将此集合转换成数组
    Iterator iterator()返回此集合中元素的迭代器。(重点方法)
  • Iterator 迭代器
    说明一下 Iterator 类,它是专为集合所准备的遍历器,其中包含3个主要方法:

    Iterator类的方法方法描述
    boolean hasNext()判断迭代器中是否有下一个元素
    E next()返回迭代器中的下一个元素并前进光标位置
    void remove()集合中移除此迭代器返回的最后一个元素(必须先用next()方法才能使用此方法)
  • Collection 方法使用
    添加元素:

    public abstract class Demo1 {
        public static void main(String[] args) {
            Collection clc = new ArrayList();
            //添加元素
            clc.add("华为");
            clc.add("苹果");
            clc.add("小米");
            clc.add("OPPO");
            //查看集合元素个数
            System.out.println("集合中有:"+clc.size()+"个元素");
            System.out.println(clc);
        }
    }
    ---*---
    输出结果:
    集合中有:4个元素
    [华为, 苹果, 小米, OPPO]
    

    删除元素:

    public abstract class Demo1 {
        public static void main(String[] args) {
            Collection clc = new ArrayList();
            //添加元素
            clc.add("华为");
            clc.add("苹果");
            clc.add("小米");
            clc.add("OPPO");
            //查看集合元素个数
            System.out.println("集合中有:"+clc.size()+"个元素");
            System.out.println(clc);
    
            //删除元素
            clc.remove("小米");
            System.out.println("集合中有:"+clc.size()+"个元素");
            clc.clear();
            System.out.println("集合中有:"+clc.size()+"个元素");
        }
    }
    ---*---
    输出结果:
    集合中有:4个元素
    [华为, 苹果, 小米, OPPO]
    集合中有:3个元素
    集合中有:0个元素
    

    判断:

    public abstract class Demo1 {
        public static void main(String[] args) {
            Collection clc = new ArrayList();
            //添加元素
            clc.add("华为");
            clc.add("苹果");
            clc.add("小米");
            clc.add("OPPO");
            //查看集合元素个数
            System.out.println("集合中有:"+clc.size()+"个元素");
            System.out.println(clc);
    
            //判断是否有此元素
            System.out.println(clc.contains("小米"));
            //判断集合是否为空
            System.out.println(clc.isEmpty());
        }
    }
    ---*---
    输出结果:
    集合中有:4个元素
    [华为, 苹果, 小米, OPPO]
    true
    false
    

    遍历:

    public abstract class Demo1 {
        public static void main(String[] args) {
            Collection clc = new ArrayList();
            //添加元素
            clc.add("华为");
            clc.add("苹果");
            clc.add("小米");
            clc.add("OPPO");
            //查看集合元素个数
            System.out.println("集合中有:"+clc.size()+"个元素");
            System.out.println(clc);
    
            //遍历元素
            System.out.println("-----增强for遍历-----");
            for (Object o: clc) {
            	String s = (String)o;
                System.out.println(o);
            }
            System.out.println("-----Iterator遍历-----");
            Iterator it = clc.iterator();
            while (it.hasNext()){
                String s = (String)it.next();
                System.out.println(s);
            }
        }
    }
    ---*---
    输出结果:
    集合中有:4个元素
    [华为, 苹果, 小米, OPPO]
    -----增强for遍历-----
    华为
    苹果
    小米
    OPPO
    -----Iterator遍历-----
    华为
    苹果
    小米
    OPPO
    

List 接口

  • 特点
    List 接口继承 Collection 接口,特点是 List 有序、有下标、元素可重复。所以这里着重介绍 List 特有方法。

  • List方法

    常用方法方法描述
    void add(int index,Object o)在index位置插入对象O
    E remove​(int index)取走集合中指定位置元素
    E set​(int index, E element)替换集合中指定位置元素
    E get(int index)返回集合中指定位置元素
    boolean addAll​(int index, Collection c)将一个集合的所有对象添加到本集合指定位置
    List subList​(int fromIndex, int toIndex)返回formIndex(包含)到toIndex(不包含)之间的元素集合
    int indexOf​(Object o)返回集合中 o 元素的下标
    ListIterator listIterator()返回此列表中元素的列表迭代器(正反迭代)
  • ListIterator 列表迭代器
    ListIterator 类继承 Iterator,它是专为List集合所准备的遍历器,可以从反方向遍历集合。下面说一下其特有方法:

    ListIterator特有方法方法描述
    boolean hasPrevious()判断当前光标的上一个位置是否有元素
    E Previous()返回当前光标的上一个位置元素并后移动光标位置
    void add(E e)将指定的元素插入集合
    void set(E e)用指定的元素替换 next()或 previous()返回的最后一个元素
    int nextIndex()返回当前光标位置数值
    int previousIndex()返回当前光标位置的上一个数值
  • List 方法使用

    通过下标插入元素:

    public class Demo {
        public static void main(String[] args) {
            List list = new ArrayList();
            list.add("苹果");
            list.add("华为");
            list.add(0,"小米");//通过下标添加元素
            System.out.println("集合元素个数为:"+list.size());
            System.out.println(list);
        }
    }
    ---*---
    输出结果:
    集合元素个数为:3
    [小米, 苹果, 华为]
    

    通过下标删除元素:

    public class Demo {
        public static void main(String[] args) {
            List list = new ArrayList();
            list.add("苹果");
            list.add("华为");
            list.add(0,"小米");
            System.out.println("删除前集合元素个数为:"+list.size());
            System.out.println("删除的元素是:"+list.remove(1));//通过下标删除元素
            System.out.println("删除后集合元素个数为:"+list.size());
            System.out.println(list);
        }
    }
    ---*---
    输出结果:
    删除前集合元素个数为:3
    删除的元素是:苹果
    删除后集合元素个数为:2
    [小米, 华为]
    

    通过下标替换元素:

    public class Demo {
        public static void main(String[] args) {
            List list = new ArrayList();
            list.add("苹果");
            list.add("华为");
            list.add(0,"小米");
            System.out.println("替换掉的元素是:"+list.set(1, "中兴"));//替换元素
            System.out.println("集合元素个数为:"+list.size());
            System.out.println(list);
        }
    }
    ---*---
    输出结果:
    替换掉的元素是:苹果
    集合元素个数为:3
    [小米, 中兴, 华为]
    

    遍历:

    public class Demo {
        public static void main(String[] args) {
            List list = new ArrayList();
            list.add("苹果");
            list.add("华为");
            list.add(0,"小米");
            System.out.println(list);
            System.out.println("----for遍历集合----");
            for (int i = 0; i < list.size(); i++) {
                String s = (String)list.get(i);
                System.out.println(s);
            }
            System.out.println("----增强for遍历集合----");
            for (Object o:list) {
                String s = (String) o;
                System.out.println(s);
            }
            System.out.println("----Iterator遍历集合----");
            Iterator it = list.iterator();
            while(it.hasNext()){
                String  s = (String) it.next();
                System.out.println(s);
            }
            System.out.println("----ListIterator遍历集合----");
            ListIterator listit = list.listIterator();
            while (listit.hasNext()){
                String s = (String) listit.next();
                System.out.println(s);
            }
            while (listit.hasPrevious()){
                String s = (String)listit.previous();
                System.out.println(s);
            }
        }
    }
    ---*---
    输出结果:
    [小米, 苹果, 华为]
    ----for遍历集合----
    小米
    苹果
    华为
    ----增强for遍历集合----
    小米
    苹果
    华为
    ----Iterator遍历集合----
    小米
    苹果
    华为
    ----ListIterator遍历集合----
    小米
    苹果
    华为
    华为
    苹果
    小米
    

    补充:增删基本数据类型

    public class Demo {
        public static void main(String[] args) {
            List list = new ArrayList();
            //添加数字自动装箱
            list.add(10);
            list.add(20);
            list.add(30);
            list.add(40);
            list.add(50);
            System.out.println(list);
            //删除操作
    //      list.remove(20); remove方法默认以下标删除
            list.remove((Object) 20);
            list.remove(Integer.valueOf(20));
            System.out.println(list);
        }
    }
    ---*---
    输出结果:
    [10, 20, 30, 40, 50]
    [10, 30, 40, 50]
    

ArrayList & LinkedList & Vector 类

ArrayList 、LinkedList 和 Vector 都实现了 List 接口( List 接口有一个 AbstractList 抽象类用来重写通用方法),所以它们的使用方法也基本是相同的。下面说一下区别:

类名存储结构适用场景线程安全
ArrayList数组结构查询快、增删慢X
LinkedList链表结构查询慢、增删快X
Vector数组结构查询快、增删慢

Set 接口

  • 特点
    Set 接口继承 Collection 接口,特点是 Set [无序]、无下标、元素不重复。Set集合没有特有方法,全继承自 Collection 接口。其根据 new 的实现类不同体现不同的特性:

    • HashSet:无序、无下标、不重复
    • LinkedHashSet:有序、无下标、不重复
    • TreeSet:定制排序、无下标、不重复
  • 元素不重复原理
    在 HashSet 对象添加元素时会进行以下两次判断:

    • 根据元素的哈希值确定存放位置,如果该位置没有元素则直接存入,如果该位置有元素先进行哈希值对比。
    • 哈希值不同,将元素存入集合。哈希值相同再调用equals进行确认,如结果为true,则拒绝元素存入集合。
  • 啥是哈希值
    1.哈希值是JDK根据对象的 地址或字符串或数字 算出来的 int类型的数值
    2.因为 Object 类中有一个 hashCode() 方法,该方法返回对象的哈希码值。所以任何对象都有哈希码值。

    //学生类
    class Student {
        private String name;
        private int age;
        public Student(String name ,int age) {
            this.name = name;
            this.age = age;
        }
    }
    //测试类
    public class Demo {
        public static void main(String[] args) {
            Student s1 = new Student("赵子龙",27);
            Student s2 = new Student("张翼德",33);
    
            System.out.println("s1的哈希值为:"+s1.hashCode());
            System.out.println("s1的哈希值为:"+s1.hashCode());
            System.out.println("s2的哈希值为:"+s2.hashCode());
        }
    }
    ---*---
    输出结果:
    s1的哈希值为:1239731077
    s1的哈希值为:1239731077
    s2的哈希值为:557041912
    

    可以看出,同一个对象的哈希码值是不变的。不同对象的哈希码值是不同的。(重写hashCode方法可以让同类型不同对象的哈希值相同

  • 重写 hashCode 方法和 equals 方法
    默认情况下同一个类的不同对象的哈希值是不同的。实际中我们希望一个类的多个对象属性不同时才视为不同元素。
    重写 hashCode 方法:为了让属性个数和值相同的对象,哈希值相同。(注意:这里不同类型的属性个数和值相同,哈希值也会相同
    重写 equals 方法:为了判对象是否是同一类型。

    //学生类
    class Student {
        private String name;
        private int age;
        public Student(String name ,int age) {
            this.name = name;
            this.age = age;
        }
    	//重写equals方法
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Student student = (Student) o;
            return age == student.age && Objects.equals(name, student.name);
        }
    	//重写hashCode方法
        @Override
        public int hashCode() {
            return Objects.hash(name, age);
        }
    }
    //测试类
    public class Demo {
        public static void main(String[] args) {
            HashSet<Student> hs = new HashSet<>();
            Student s1 = new Student("赵子龙",27);
            Student s2 = new Student("张翼德",33);
            Student s3 = new Student("张翼德",33);
            hs.add(s1);//成功添加
            hs.add(s2);//成功添加
            hs.add(s3);
            hs.add(s1);
            System.out.println("集合元素个数:"+hs.size());
            System.out.println("s1的哈希值为:"+s1.hashCode());
            System.out.println("s2的哈希值为:"+s2.hashCode());
            System.out.println("s3的哈希值为:"+s3.hashCode());
        }
    }
    ---*---
    输出结果:
    集合元素个数:2
    s1的哈希值为:1102553374
    s2的哈希值为:757717223
    s3的哈希值为:757717223
    

    重写了hashCode方法和equals方法,当哈希值相同而且还是同一对象时。我们就视为同一元素,不存储该元素。

  • Set 集合存储并遍历

    public class Demo {
        public static void main(String[] args) {
            Set<String> ss = new HashSet<>();
            //添加元素
            ss.add("1.苹果");
            ss.add("2.华为");
            ss.add("3.vivo");
            ss.add("3.vivo");//来一个重复的元素
            ss.add("4.菠萝");
            //遍历
            for (String str : ss){
                String s1 = str;
                System.out.println(s1);
            }
        }
    }
    ---*---
    输出结果:
    3.vivo
    1.苹果
    2.华为
    4.菠萝	//可以看出元素无序且不重复
    

    结果可以看出HashSet集合存储元素是无序的并且不重复。


HashSet & LinkedHashSet & TreeSet

类名存储结构适用场景
HashSet哈希表(数组+链表)不重复、无序
TreeSet红黑树不重复、定制排序
LinkdeHashSet哈希表+链表不重复、有序
HashSet 类
  • 特点
    HashSet集合是用哈希表存储的,可以理解为元素是链表的数组。由于存储的位置是由哈希值确定的,所以是无序的。
    public class Demo {
        public static void main(String[] args) {
            Set<String> ss = new HashSet<>();
            ss.add("1.苹果");
            ss.add("2.华为");
            ss.add("3.vivo");
            ss.add("4.三星");
            ss.add("4.三星");
            //遍历
            Iterator<String> it = ss.iterator();
            while (it.hasNext()){
                String s = it.next();
                System.out.println(s);
            }
        }
    }
    ---*---
    输出结果:
    3.vivo
    4.三星
    1.苹果
    2.华为
    
TreeSet 类
  • 特点

    1. 基于排列顺序实现元素不重复。
    2. 元素按照一定规则进行排序,具体取决与构造方法。
         .TreeSet():根据元素的自然排序进行排序
         .TreeSet(Comparator c) :根据指定比较器排序
  • TreeSet 存储结构
    Tree翻译是树的意思,TreeSet以节点形式存储数据,每个节点可有两个子节点。当存储数据时,第一个数据将存储在根节点上,之后的数据都会和根节点比较,比根节点小存入根节点左边子节点,比根节点大存入根节点右边子节点,和根节点相等则视为相同数据不存储。当子节点已经有数据时,存入数据和子节点数据进行比较,比子节点小存左,比子节点大存右。以此类推,找到没有数据的节点。

  • 无参构造存放学生对象并遍历(自然排序)

    //学生类
    class Student implements Comparable<Student>{
         String name;
         int age;
    
        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }
        //该方法用于判断是否元素相同和存储位置
        @Override
        public int compareTo(Student s) {
            int num =  this.age-s.age;
            int num2 = num==0? this.name.compareTo(s.name):num;
            return num2;
        }
    }
    //测试类
    public class Demo {
        public static void main(String[] args) {
    
            TreeSet<Student> trs = new TreeSet<>();
            Student s1 = new Student("zhanglei", 18);
            Student s2 = new Student("xiaohong", 8);
            Student s3 = new Student("renxianqi", 38);
            Student s4 = new Student("wanglihong", 38);
            trs.add(s1);//如果没有继承Comparable则抛出异常
            trs.add(s2);
            trs.add(s3);
            trs.add(s4);
            trs.add(s4);
            System.out.println("集合中元素个数:"+trs.size());
            for (Student stu: trs){
                System.out.println(stu.name+","+stu.age);
            }
        }
    }
    ---*---
    输出结果:
    集合中元素个数:4
    xiaohong,8
    zhanglei,18
    renxianqi,38
    wanglihong,38
    

    注意:使用TreeSet集合无参构造创建的对象添加元素,需要元素对象的类型必须实现Comparable接口,重写CompareTo方法(方法用于判断元素是否重复和元素存放位置)。

  • 带参构造存放学生对象并遍历(比较器排序)

    //学生类
     class Student {
         String name;
         int age;
    
        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }
    }
    //测试类
    public class Demo {
        public static void main(String[] args) {
            TreeSet<Student> trs = new TreeSet<>(new Comparator<Student>() {
            	//该方法和CompareTo功能一样
                @Override
                public int compare(Student o1, Student o2) {
                    int num = o1.age - o2.age;
                    int num2 = num==0?o1.name.compareTo(o2.name):num;
                    return num2;
                }
            });
            Student s1 = new Student("zhanglei", 18);
            Student s2 = new Student("xiaohong", 8);
            Student s3 = new Student("renxianqi", 38);
            Student s4 = new Student("wanglihong", 38);
            trs.add(s1);
            trs.add(s2);
            trs.add(s3);
            trs.add(s4);
            trs.add(s4);
            System.out.println("集合中元素个数:"+trs.size());
            for (Student stu: trs){
                System.out.println(stu.name+","+stu.age);
            }
        }
    }
    ---*---
    输出结果:
    集合中元素个数:4
    xiaohong,8
    zhanglei,18
    renxianqi,38
    wanglihong,38
    

    使用比较器排序可以实现和自然排序一样的功能,区别是自然排序需要在元素类型中实现Comparable接口,比较器是在TreeSet构造方法中传一个Comparator。

  • Comparable 和 Comparator 接口
    使用TreeSet集合存储元素,需要元素对象类型实现Comparable接口重写其compareTo方法或者使用TreeSet的带参构造传入一个Comparator(比较器)对象。两者其本质是一样的,需要将存入元素和已有元素进行对比,以确定存入的元素是否重复,和存放的位置。(具体参考上面的 TreeSet 存储结构)。

    public class Demo {
        public static void main(String[] args) {
            TreeSet<Integer> is = new TreeSet<>(new Comparator<Integer>() {
                @Override
                public int compare(Integer o1, Integer o2) {
                    return o2 - o1;//o2-o1反序 --- o1-o2正序
                }
            });
            is.add(20);
            is.add(50);
            is.add(10);
            is.add(30);
            is.add(40);
            for (Integer ii : is) {
                System.out.println(ii);
            }
        }
    }
    ---*---
    输出结果:
    50
    40
    30
    20
    10
    

    上面是调用TreeSet带参构造创建的集合对象,存储Integer对象。着重说明一下Comparator中的compare方法。

    public int compare(Integer o1, Integer o2) { return o2-o1;}

    该方法会返回一个int类型的值,当集合存储元素时,会把存即将入元素(o1)和已有元素(o2)进行比较。o1-o2=0说明两元素相同不进行存储,o1-o2>0将o1存储到o2元素后面,o1-o2<0将o1存储到o2元素前面。

  • 练习:TreeSet存储字符串元素,以长短排序,相同长度以字母排序。

    public class Demo1  {
        public static void main(String[] args) {
        	//带参构造创建集合对象
            TreeSet<String> ss = new TreeSet<>(new Comparator<String>() {
            	//重写compare方法
                @Override
                public int compare(String o1, String o2) {
                    int num = o1.length()-o2.length();
                    int num2 = num==0?o1.compareTo(o2):num;
                    return num2;
                }
            });
    		//添加元素
            ss.add("xyz");
            ss.add("abc");
            ss.add("xz");
            ss.add("x");
            ss.add("xysdsz");
            ss.add("xyaaadfz");
            ss.add("aab");
            ss.add("aaa");
            for (String s:ss ) {
                System.out.println(s);
            }
        }
    }
    ---*---
    输出结果:
    x
    xz
    aaa
    aab
    abc
    xyz
    xysdsz
    xyaaadfz
    
LinkdeHashSet 类
  • 特点
    LinkdeHashSet集合的存储结构是哈希表+链表实现的,其存储元素不重复且有序。有序是由链表实现的。

    public class Demo {
        public static void main(String[] args) {
            LinkedHashSet<String> ss = new LinkedHashSet<>();
            ss.add("1.苹果");
            ss.add("2.华为");
            ss.add("3.vivo");
            ss.add("4.三星");
            ss.add("4.三星");
            //遍历
            for (String str : ss){
                System.out.println(str);
            }
        }
    }
    ---*---
    输出结果:
    1.苹果
    2.华为
    3.vivo
    4.三星
    

    LinkedHashSet 算是 HashSet 的增强版,让元素不重复并且实现了存取顺序一致。


并发修改异常

  • 概述
    简单来说就是,集合中的元素个数修改时会有一个A变量计数,创建迭代器时将集合中的A赋值给了迭代器中的变量B。在迭代器调用next()方法时会先判断A是否等于B,如果不等于就抛出并发修改异常。所以在创建迭代器之后不能使用集合中的方法进行增删元素,只能使用迭代器中的方法增删。

  • 演示代码

    import java.util.ArrayList;
    import java.util.Iterator;
    import java.util.List;
    public class Demo {
        public static void main(String[] args) {
            List<String> list = new ArrayList<>();
            list.add("苹果");
    
            //拿到该集合的迭代器
            Iterator<String> iterator = list.iterator();
            //修改集合元素个数
            list.add("榴莲");
    
            while (iterator.hasNext()){
                String s = iterator.next();//此处报错 
                System.out.println(s);
            }
        }
    }
    ---*---
    输出结果:
    Exception in thread "main" java.util.ConcurrentModificationException
    	at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1043)
    	at java.base/java.util.ArrayList$Itr.next(ArrayList.java:997)
    	at com.Zy.Demo.main(Demo.java:17)
    

    解决办法:使用增强for代替Iterator


Map 接口

创建 Map 对象需要通过多态的形式,其根据 new 的实现类不同体现不同的特性:

  1. HashMap:无序、无下标、(键不重复,值可重复)
  2. LinkedHashMap:有序、无下标、(键不重复,值可重复)
  3. TreeMap:定制排序、无下标、(键不重复,值可重复)
  • Map 常用方法

    常用方法名描述
    V put(K kkey,V value)添加元素
    V remove(Object key)根据键删除键值对元素
    void clear()删除集合所有元素
    boolean containsKey(Object key)判断集合是否包含指定的键
    boolean containsValue(Object value)判断集合是否包含指定的值
    boolean isEmpty()判断集合是否为空
    int size()返回集合长度
    V get(Object key)根据键获取值
    Set< K > keySet()获取所有键的集合
    Collection< V > values()获取所有值的集合
    Set< Map.Entry< K,V >>  entrySet()获取所有键值对对象的集合
  • 方法练习
    添加元素(提示:添加元素出现键相同值不同时,值将被替换。)

    public class Demo  {
        public static void main(String[] args) {
            Map<String,String> map = new HashMap<>();
            map.put("cn","中国");
            map.put("usa","美国");
            map.put("br","巴西");
            map.put("cu","古巴");
            map.put("cu","巴古");
            System.out.println(map);
        }
    }
    ---*---
    输出结果:
    {br=巴西, usa=美国, cu=巴古, cn=中国}
    

    删除元素

    public class Demo  {
        public static void main(String[] args) {
            Map<String,String> map = new HashMap<>();
            map.put("cn","中国");
            map.put("usa","美国");
            map.put("br","巴西");
            map.put("cu","古巴");
            System.out.println("集合元素个数:"+map.size());
            map.remove("usa");
            System.out.println("删除后元素个数:"+map.size());
        }
    }
    ---*---
    输出结果:
    集合元素个数:4
    删除后元素个数:3
    

    遍历

    public class Demo  {
        public static void main(String[] args) {
            Map<String,String> map = new HashMap<>();
            map.put("cn","中国");
            map.put("usa","美国");
            map.put("br","巴西");
            map.put("cu","古巴");
            System.out.println("集合元素个数:"+map.size());
            //keySet遍历
            Set<String> strings = map.keySet();
            for (String s:strings  ) {//strings可以替换为map.keySet()
                System.out.println(s+","+map.get(s));
            }
            System.out.println("--------");
            //entrySet遍历
            Set<Map.Entry<String, String>> entries = map.entrySet();
            for (Map.Entry<String,String> ii: entries){entries可以替换为map.entrySet()
                System.out.println(ii.getKey()+","+ii.getValue());
            }
        }
    }
    ---*---
    输出结果:
    集合元素个数:4
    br,巴西
    usa,美国
    cu,古巴
    cn,中国
    --------
    br,巴西
    usa,美国
    cu,古巴
    cn,中国
    
  • 添加学生对象并遍历(为了简洁学生类重写了toString方法)

    public class Demo  {
        public static void main(String[] args) {
            Map<String,Student> map = new HashMap<>();
            //创建学生对象
            Student s1 = new Student("张磊", 15);
            Student s2 = new Student("小明", 18);
            Student s3 = new Student("小黑", 20);
            //添加元素
            map.put("001",s1);
            map.put("002",s2);
            map.put("003",s3);
            //遍历
            for (Map.Entry<String,Student> ii:map.entrySet()){
                String key = ii.getKey();
                Student value = ii.getValue();
    
                System.out.println("学号"+key+"\t"+value);
            }
        }
    }
    ---*---
    输出结果:
    学号001	Student{name='张磊', age=15}
    学号002	Student{name='小明', age=18}
    学号003	Student{name='小黑', age=20}
    

HashMap & LinkedHashMap & TreeMap

HashMap 和 HashSet 一样要想实现存入键的元素不重复需要重写键元素对象类的hashCode方法和equals方法。

LinkedHashMap 和 LinkedHashSet一样,算是 HashMap 的增强版,让键元素不重复并且实现了存取顺序一致。

TreeMap 和 TreeSet 一样要想实现存入键的元素不重复需要键元素对象类型实现Comparable接口或者使用TreeMap的带参构造传入一个Comparator(比较器)对象。

  • HashMap添加学生对象和住址

    public class Demo  {
        public static void main(String[] args) {
            HashMap<Student,String> treeMap = new HashMap<>();
            Student s1 = new Student("张磊", 15);
            Student s2 = new Student("小明", 38);
            Student s3 = new Student("小黑", 20);
            Student s4 = new Student("小黑", 20);//相同属性对象s3 s4
    
            treeMap.put(s2,"重庆");
            treeMap.put(s1,"北京");
            treeMap.put(s3,"上海");
            treeMap.put(s4,"无锡");//添加相同键值对象
    
            //遍历
            for (Map.Entry<Student,String> ii:treeMap.entrySet()){
                Student key = ii.getKey();
                String value = ii.getValue();
                System.out.println(key+"居住在:"+value);
            }
        }
    }
    ---*---
    输出结果:
    学生{姓名='张磊', 年龄=15}居住在:北京
    学生{姓名='小明', 年龄=38}居住在:重庆
    学生{姓名='小黑', 年龄=20}居住在:无锡
    
  • TreeMap添加学生对象和住址(按学生年龄排序)

    public class Demo  {
        public static void main(String[] args) {
            TreeMap<Student,String> treeMap = new TreeMap<>(new Comparator<Student>() {
                @Override
                public int compare(Student o1, Student o2) {
                    int num = o1.age-o2.age;//学生年龄升序
                    int num2 = num==0?o1.name.compareTo(o2.name):num;//年龄相同按字符排列
                    return num2;
                }
            });
            Student s1 = new Student("张磊", 15);
            Student s2 = new Student("小明", 38);
            Student s3 = new Student("小黑", 20);
            Student s4 = new Student("小黑", 20);//相同属性对象s3 s4
    
            treeMap.put(s2,"重庆");
            treeMap.put(s1,"北京");
            treeMap.put(s3,"上海");
            treeMap.put(s4,"江津");//添加相同键值对象
    
            //遍历
            for (Map.Entry<Student,String> ii:treeMap.entrySet()){
                Student key = ii.getKey();
                String value = ii.getValue();
                System.out.println(key+"居住在:"+value);
            }
        }
    }
    ---*---
    输出结果:
    学生{姓名='张磊', 年龄=15}居住在:北京
    学生{姓名='小黑', 年龄=20}居住在:江津
    学生{姓名='小明', 年龄=38}居住在:重庆
    
  • 列表集合存储HashMap元素遍历

    public class Demo  {
        public static void main(String[] args) {
            ArrayList<HashMap<String,String>> aa = new ArrayList<>();
            HashMap<String,String> hh1 = new HashMap<>();
            HashMap<String,String> hh2 = new HashMap<>();
            HashMap<String,String> hh3 = new HashMap<>();
            hh1.put("张无忌","赵敏");
            hh1.put("林心如","霍建华");
            hh2.put("赵丽颖","冯绍峰");
            hh3.put("刘欣","邵峰");
            aa.add(hh1);
            aa.add(hh2);
            aa.add(hh3);
            //遍历
            for (HashMap<String,String> hh: aa){
                for (Map.Entry<String,String> mm: hh.entrySet()){
                    System.out.println(mm.getKey()+","+mm.getValue());
                }
            }
        }
    }
    ---*---
    输出结果:
    林心如,霍建华
    张无忌,赵敏
    赵丽颖,冯绍峰
    刘欣,邵峰
    

泛型

   将一个对象放入集合中时,由于集合并不知道我们要放什么类型,所以统统由 Object 类型变量接收。当我们在次使用对象的时候,对象变为Object类型,我们必须自己转换其类型。为了解决这个问题,则提出泛型。
泛型本质是参数化类型,将数据类型作为参数传递可用于类、接口和方法上。

  • 格式<T,…>:T视为一个数据类型占位符,表示一种引用类型。(可以定义多个泛型,用逗号隔开)

  • 使用泛型的好处
    1.提供编译时类型检测
    2.避免了强制类型转换
    3.提高了代码的灵活性

泛型类

  • 泛型类格式:
    格式:修饰符 class 类名 < 类型 > { }
    范例:public class Generic < T > { }

  • //泛型类
    class Student <T> {
        T age;
        public T getAge() {
            return age;
        }
        public void setAge(T age) {
            this.age = age;
        }
    }
    //测试类
    public class Demo {
        public static void main(String[] args) {
            //传入Integer类型
            Student<Integer> stu1 = new Student<>();
            stu1.setAge(5);
            System.out.println("传入Integer类型:"+stu1.getAge());
    
            //传入String类型
            Student<String> stu2 = new Student<>();
            stu2.setAge("8");
            System.out.println("传入String类型:"+stu2.getAge());
        }
    }
    ---*---
    输出结果:
    传入Integer类型:5
    传入String类型:8
    

    注意:当泛型类被实例化或者被继承时,如果没有传入确定的类型,则该泛型将被当做Object类型看待。

泛型接口

  • 泛型接口格式:
    格式:修饰符 interface 接口名 < 类型 > { }
    范例:public interface Generic < T > { }

  • //泛型接口
    interface Genercic <T> {
        void show(T t);
    }
    //实现类
    class Genericimpl<T> implements Genercic<T>{
        @Override
        public void show(T t) {
            System.out.println(t);
        }
    }
    //测试类
    public class Demo {
        public static void main(String[] args) {
            Genericimpl<String> s = new Genericimpl<>();
            s1.show("苹果");
            Genericimpl<Integer> i = new Genericimpl<>();
            s2.show(50);
        }
    }
    ---*----
    输出结果:
    苹果
    50
    

    注意:泛型接口的实现类应该是一个泛型类,注意该类的格式。它定义了一个和接口一样的泛型。

泛型方法

  • 泛型方法格式:
    格式:修饰符 < 类型 > 返回值类型 方法名(类型 变量名) { }
    范例:public < T > void show(T t) { }

  • //测试类
    public class Demo {
        //泛型方法
        public static  <T> T show(T t){
            System.out.println(t);
            return t;
        }
        //main方法
        public static void main(String[] args) {
            Demo.show("青岛");
            Demo.show(100);
            Demo.show(12.5);
            Demo.show(true);
        }
    }
    ---*---
    输出结果:
    青岛
    100
    12.5
    true
    

    很明显,泛型方法可以用更少的代码实现方法的重载。方法传入类型的时候才确定泛型的类型,也就是说传入什么类型就是什么类型。


Collections 集合工具类

  • 概述
    针对集合的工具类,其中均是静态方法,可通过类名直接调用。
  • 常用方法
方法描述
<T extends Comparable<? super T>> void sort(List list)将制定的列表按升序排序
< T > void sort​(List list, Comparator<? super T> c)根据指定比较器引发的顺序对指定列表进行排序
void reverse(List<?>list)翻转制定列表中元素的顺序
void shuffle(List<?> list)使用默认的随机源随机排列指定的列表
< T > void fill​(List<? super T> list, T obj)用指定的元素替换指定列表的所有元素

Collections更多方法


练习题

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值