6.1 常见集合与应用



一、初始集合与应用

1.1 周介绍

1.学习六部分内容:

(1)常见集合与应用

(2)泛型及高阶应用

(3)多线程的安全与应用

(4)通过I/O实现文件的读取与写入

(5)综合案例-多线程下载器

(6)JDK不同版本的新特性-基础篇

2.重要知识点:

(1)常见集合与应用

集合,是利用多种数据结构,代替数组,存储多个数据。

(2)泛型及高阶应用

(3)多线程的安全与应用

很多初学者的难点。

(4)通过I/O实现文件的读取与写入

(5)综合案例-多线程下载器

(6)JDK不同版本的新特性-基础篇

随着时代发展,现在用的JDK一定会被淘汰。学习新的语法新特性,有助于解决实际开发中的问题。

1.2 课程介绍

本章主要学习常见集合与应用。分四部分内容:

(1)List集合体系及应用

(2)Set集合体系及应用

(3)Map映射体系及其应用

(4)应用Collections实现集合排序

1.3 初识集合与应用的使用场景

1.集合概念

以前,进行数据存储的是数组。缺点是创建数组时必须声明确定数组的长度。但是,实际应用中,一开始并不确定数组有多少个元素。

现在,Java中有一些工具类,可以用来替代数组,弥补上述缺点,称为集合类。

集合类包含如下四个体系:

  • List
  • Set
  • Map
  • Queue------队列

集合类主要由两个接口派生而出:

  • Collection
  • Map

2.四种存储结构

  • List------代表有序、可重复的集合
  • Set------代表无序、不可重复的集合
  • Map-----代表存储映射关系的集合
  • Queue--代表队列的特性

3.Collection接口及实现

  • Collection-----是所有单个数值,数据结构的顶层接口

4.Map接口及实现

以上是粗略的知识脉络。

1.4 IDEA编译器的安装与项目创建

1.IDEA编译器

以前,用的是Eclipse。现在,用IDEA。

全程IntelliJ IDEA,是java编程公认最好的IDE。

特点:

  • 功能更强大,提供大量智能工具,例如语法提示、代码分析、格式化。
  • 最核心的特色:快捷键,能极大提高开发效率

2.IDEA的安装

(1)JDK8的安装

前置要求:电脑上要安装JDK8及以上版本

目前,国际及中国范围内,70%以上的软件项目,仍然使用java8进行。

在系统cmd命令行中,检查JDK是否安装成功:

(2)安装IntelliJ IDEA

国内主流用的版本是2020:

勾选:

成功安装:

二、List集合体系及应用

2.1 List接口

1.List集合:

元素有序、可重复的集合,每个元素都有其对应的顺序索引。

  • 有序:天然按照放入时的前后顺序,进行排列
  • 重复:元素可以重名,因为可以索引访问指定位置的元素

2.图示:

实际开发中,对于List接口,应用最多的是ArrayListLinkedList两种实现类。

2.2 ArrayList实现类的特点及方法应用

1.特点

  • 基于数组实现的List类,是Java数组的有效替代品
  • 自动扩容,多数情况下不用指定最大长度
  • 数据在内存中的存储是紧密连续的,因此,数据访问速度非常快
一般,数组就要在实例化数组的时候必须指定数组的长度。

2.使用方法

  • add()
  • get()
import java.util.ArrayList;                //所有关于集合的类和接口,都统一存储在java.util工具包下
...
ArrayList<String> bookList=new ArrayList<String>();        //实例化类ArrayList        //<String>:说明保存在bookList对象里的数据都是字符串        //不用指定大小
bookList.add("三国演义");                //新增元素,尾部追加
String bookName1=bookList.get(0);        //获取第一个元素,索引从0开始

3.程序操作tips

(1)想回到工程的默认首界面:选择界面

File--->Close Project

(2)调整目录结构显示方式

让目录结构更清晰

(3)创建类

4.ArrayList`实现类的方法应用

  • add()方法

    IDEA源代码并不需要像Eclipse那样,手动保存文件。因为文件变化时,IDEA会自动帮我们保存文件

package com.imooc.collection.list;
import java.util.ArrayList;

public class ArrayListSample {
    public static void main(String[] args) {        //"psvm":用IntelliJ IDEA的快捷键来生成主方法:
        ArrayList<String> bookList=new ArrayList<String>();
        bookList.add("三国演义");        //"Ctrl+Shift+回车":自动增加结尾的分号;如果有分号,就自动回车,保证连续书写
        bookList.add("水浒传");
        bookList.add("三国演义");
        System.out.println(bookList);           //"sout":自动生成打印输出的代码段
    }
}

--->
[三国演义, 水浒传, 三国演义]        //List集合的元素有序、允许重复的体现
  • get()方法

  • add()方法:第二种应用
  • set()方法
public class ArrayListSample {
    public static void main(String[] args) {           
        ArrayList<String> bookList=new ArrayList<String>();
        //add方法1:直接插入元素,默认尾部追加
        bookList.add("三国演义");                       
        bookList.add("水浒传");
        System.out.println(bookList); 
        //get方法:
        String bookName1=bookList.get(1);
        System.out.println(bookName1);
//        bookList.get(10);                             //"Ctrl+/":注释当前行
        //add方法2:在指定位置,插入新的数据
        bookList.add(1,"红楼梦");      
        System.out.println(bookList);
        //add方法,返回值是bool型
        Boolean result1 = bookList.add("西游记");
        System.out.println("列表是否发生变化:" + result1);
       
        //set方法:更新设置
        String before = bookList.set(3, "西游记后传");       
        System.out.println(before);
        System.out.println(bookList);      
    }
}

--->
[三国演义, 水浒传]
水浒传
[三国演义, 红楼梦, 水浒传]
列表是否发生变化:true
    
西游记
[三国演义, 红楼梦, 水浒传, 西游记后传]

除了get()方法,上述的add(int,Object)方法也会出现如下错误:数组下标越界:IndexOutOfBoundsException异常

原因:向不连续的位置,添加元素。如下:

底层原理:ArrayList底层是数组。它会对元素的下标(index)和当前集合的元素数量(size)进行对比,如果下标比元素数量多,就会被认为是不合法。

应对措施:在添加元素之前,先判断下标是否超出范围。如下:

  • remove()方法
  • size()方法
public class ArrayListSample {
    public static void main(String[] args) {           
        ArrayList<String> bookList=new ArrayList<String>();
        bookList:[三国演义, 红楼梦, 水浒传, 西游记后传]
            
        //remove的用法1:直接删除对象,返回的是bool值
        boolean result2 = bookList.remove("西游记后传");
        System.out.println(result2);
        System.out.println(bookList);
        //remove的用法2:删除某索引位置上的元素,返回的是被删除的元素
        String item = bookList.remove(0);//传入的是具体的索引位置
        System.out.println(item);
        System.out.println(bookList);
        
        //size:求元素的数量
        int count = bookList.size();
        System.out.println(count);
        //包含size方法的复合方式
        bookList.set(bookList.size() - 1, "测试数据");              //与set更新方法相结合
        System.out.println(bookList);
        bookList.remove(bookList.size() - 1);                    //与remove移除方法想结合
        System.out.println(bookList);
    }
}

--->
true
[三国演义, 红楼梦, 水浒传]        //用法1:移除元素成功
    
三国演义
[红楼梦, 水浒传]                  //用法2:移除元素成功
    
2
[红楼梦, 测试数据]
[红楼梦]

2.3 LinkedList实现类的特点及方法应用

1.特点:

  • 实现了Deque和List两个接口;
  • 在元素有序、允许重复的基础上,可以作为队列在队首、队尾快速追加数据
  • 底层是链表,所以数据的存储是分散的。数据插入速度快,但是数据访问速度慢

2.方法应用

从使用角度,与ArrayList实现类的方法应用基本相同。

public class LinkedListSample {
    public static void main(String[] args) {
        LinkedList<String> bookList = new LinkedList<String>();
        bookList.add("三国演义");
        bookList.add(0,"水浒传");                    //"Ctrl+D":复制当前行
        bookList.add("西游记");
        bookList.add("红楼梦");

        System.out.println(bookList);

        bookList.addFirst("蒸汽革命");
        bookList.addLast("黄金时代");
        System.out.println(bookList);
    }
}

--->
[水浒传, 三国演义, 西游记, 红楼梦]
[蒸汽革命, 水浒传, 三国演义, 西游记, 红楼梦, 黄金时代]

通过查看源码,追溯上述方法的源头:

按住Ctrtl+鼠标点击想要查看源码的类:

但是,从底层的数据存储上,却是大不相同。

3.ArrayListLinkedList实现类的存储结构对比

ArrayList实现类:本质是数组

特点:物理存储连续紧密---->数据的访问读取速度非常快

适用场景:数据比较稳定,需要大量的数据读取

LinkedList实现类:本质是链表

特点:物理存储分散,中间用指针来连接---->数据的插入新增速度非常快

适用场景:频繁的数据插入

2.3 通过三种不同的遍历方式获取集合中的数据

1.增强型for循环

public class ListLoopSample {
    public static void main(String[] args) {
        List<String> bookList=new ArrayList<String>();     //将接口的引用,指向接口的实现类
        bookList.add("三国演义");
        bookList.add("水浒传");
        bookList.add("西游记");
        bookList.add("红楼梦");                            //对象的初始化:添加数据

        for(String book:bookList){                        //遍历方式1
            System.out.println(book);
        }
    }
}

--->
三国演义
水浒传
西游记
红楼梦

2.forEach方法遍历

利用对象自带的forEach方法+配合基于JDK1.8中的lambda表达式

写法简洁,用的最多。

public class ListLoopSample {
    public static void main(String[] args) {
        List<String> bookList=new ArrayList<String>();
        bookList.add("三国演义");
        bookList.add("水浒传");
        bookList.add("西游记");
        bookList.add("红楼梦");                           

        bookList.forEach(book->{                        //遍历方式2
            System.out.println(book);
        });
    }
}

--->
三国演义
水浒传
西游记
红楼梦

3.Iterator迭代器对象遍历

目前用的越来越少。

迭代器本身的使用是一次性的。如果需要多次使用迭代器对集合进行遍历,那么每次都得需要重新获取迭代器对象。

public class ListLoopSample {
    public static void main(String[] args) {
        List<String> bookList=new ArrayList<String>();
        bookList.add("三国演义");
        bookList.add("水浒传");
        bookList.add("西游记");
        bookList.add("红楼梦");                            
        
        Iterator<String> itr = bookList.iterator();        //指针一开始指向第一个元素的前面        //遍历方式3
        while(itr.hasNext()){               //是否还有下一个元素
            String book=itr.next();         //获取下一个元素,同时将指针向后移动
            System.out.println(book);
        }
    }
}

--->
三国演义
水浒传
西游记
红楼梦

三、Set集合体系及应用

3.1 Set接口

Set集合:元素无序、不可重复的集合

Set集合与List集合使用方法基本相同

两种最常见的实现类:HashSet与TreeSet

Set集合的体系图示:

3.2 HashSet实现类的特点及方法应用

public class HashSetSample {
    public static void main(String[] args) {
        Set<String> mobileSet=new HashSet<String>();        //前面是接口,后面是实现类
        mobileSet.add("13311112222");
        mobileSet.add("13333334444");
        mobileSet.add("13355556666");

        System.out.println(mobileSet);
        
        boolean isChanged=mobileSet.add("13377778888");
        System.out.println("Set集合是否发生改变:"+isChanged);

        isChanged=mobileSet.add("13377778888");
        System.out.println("Set集合是否发生改变:"+isChanged);    //对于重复的数据,二次写入会失败
        
        System.out.println(mobileSet);
        //Set集合可以使用所有Collection接口定义的方法
        int count = mobileSet.size();
        boolean result = mobileSet.contains("13377778888");
        System.out.println(result);
    }
}
--->
[13333334444, 13355556666, 13311112222]                  //证明:Set集合是元素无序的
Set集合是否发生改变:true
Set集合是否发生改变:false                               //证明:Set集合是不允许重复的
[13333334444, 13355556666, 13377778888, 13311112222]
true

源码查看:

因为是元素无序,所以,所有通过下标索引操作数据的方法,都是不支持的。

也就是说,get等索引获取数据的方法属于List接口,因此Set实现类无法使用。

3.3 Set集合的数据的唯一性

1.Set集合如何确保数据的唯一性?

Set集合在新增数据时,先判断数据的hashCode( )是否存在,

若hashCode( )在Set集合存在,再调用equals( )进行比较;

若hashCode( )和equals( )都存在的情况下,Set集合才认为数据已存在,不予新增。

2.为什么要用对象的hashCode( ),直接用equals( )判断不行吗?

出于执行效率考虑。

hashCode( )返回的整数结果,决定了Set集合中的存放位置,hashCode( )计算速度很快(可能出现哈希碰撞);

equals( )则是对值进行比较,处理速度相对较慢。

每个实现类,都有HashCode方法。类不同,方法也不一样。

3.4 HashSet与TreeSet的存储原理

1.HashSet

(1)特点:

  • HashSet是Set接口的典型实现,使用最多
  • HashSet按照Hash算法来决定集合元素的顺序,具有很好的查找性能
  • 存储位置由对象的hashCode散列值来决定

(2)Hash的概念:

哈希、散列、杂凑,目的是把任意长度的数据,通过哈希算法变换成固定的输出。该输出就是散列值。

int hash=(h="a".hashCode())^(h>>>16);
--->
97

(3)HashSet示例:

Set set=new HashSet();
set.add("d");
set.add("a");
set.add("b");
set.add("c");

System.out.println(set);
--->
[a,b,c,d]            //按照Hash算法来决定集合元素的顺序,

底层原理:只介绍HashSet存储顺序的逻辑:

底层有个算法,通过不同的散列值,就能得到稳定的数组的存放位置。

2.LinkedHashSet:

  • 是HashSet的子类。除了具有HashSet的特性外,同时使用链表维护元素的次序,以保障按照插入顺序来提取数据。

3.TreeSet:

  • 是SortedSet接口的实现类,用于保证集合元素处于排序状态
  • 利用红黑树的数据结构,来存储集合元素
  • 默认采用自然排序,对集合元素升序排列,也可以实现Comparable接口自定义排序方法

3.5 HashSet与TreeSet的代码应用

1.HashSet

案例演示LinkedHashSet按照我们插入时的顺序,对Set集合中的数据进行提取

public class LinkedHashSetSample {
    public static void main(String[] args) {

        //案例演示LinkedHashSet按照我们插入时的顺序,对Set集合中的数据进行提取
        Set<String> mobileSet=new LinkedHashSet<String>();
        mobileSet.add("13311112222");
        mobileSet.add("13333334444");
        mobileSet.add("13355556666");
        mobileSet.add("13377778888");
        System.out.println(mobileSet);
    }
}
--->
[13311112222, 13333334444, 13355556666, 13377778888]

2.TreeSet

  • 案例演示TreeSet默认按照对象的字面数值、文本的字面数值,来排序
public class TreeSetSample {
    public static void main(String[] args) {
   //案例演示TreeSet按照对象的字面数值、文本的字面数值,来排序
        Set<Integer> set = new TreeSet<Integer>();//Integer通过包装类的形式,保存整型数据
        set.add(100);
        set.add(140);
        set.add(180);
        set.add(200);
        System.out.println(set);
    }
}

--->
[100, 140, 180, 200]        //TreeSet默认采用自然排序
  • 当然,案例演示TreeSet如何按照自定义规则进行排序
public class TreeSetSample {
    
    //新建一个内部类,重写比较方法,自定义比较规则
    class IntegerComparator implements Comparator<Integer>{     
        @Override
        public int compare(Integer o1, Integer o2) {    //通过上面Comparator接口,来提醒程序员这里必须重写接口中的抽象方法
            return o2-o1;       //降序排列
        }
    }

    public void sort(){
        Set<Integer> set = new TreeSet(new IntegerComparator());       //构造方法中传入实例
        set.add(100);
        set.add(140);
        set.add(180);
        set.add(200);
        System.out.println(set);
    }

    public static void main(String[] args) {
    //案例演示TreeSet如何按照自定义规则进行排序
        new TreeSetSample().sort();
    }
}

--->
[200, 180, 140, 100]        //TreeSet成功的自定义规则进行排序

四、Map集合体系及其应用

以上讲的List和Set集合,存储的往往是单个数据本身。但是,在生活中,除了要存取数据本身,还要存取数据的含义。

生活中的映射:数据-数据对应的含义

如果放在java语言中,如何表达这种映射关系呢?

Map映射,应运而生。

有点像python中的字典 :)

4.1 初识Map接口及Hash Map实现类的常用方法

1.Map映射的特点

(1)Map用于保存具有映射关系的数据。每组映射都是Key(键)与Value(值)组合而成。

叫法:键值对,或kv

Map就像一整张登记表,一个Map中包含很多个不同的键值对

(2)键值对 可以是任意数据类型,但是Key通常时String

(3)key是全局唯一、不可重复.一旦重复,后者覆盖前者。

2.Map体系

最常用的是HashMap。

  • HashMap是Map接口的典型实现类,它保证对Key进行无序、唯一的存储
  • 本质就是Hash对Key进行算法处理,换算成固定的输出

3.HashMap与HashSet的关系与区别:

Java先有Map后有Set,HashSet从HashMap精简而来。正是这样的设计,会发现两者是高度相似的。

  • HashMap

  • HashSet

    可以理解为,下面传入的只是键值对的Key,而PRESENT其实是个占位符,一个固定的静态常量。

4.2 Map实现类:HashMap与LinkedMap的区别

HashMap实现类是Map映射体系下,使用最多的实现类。

1.HashMap实现类的使用方法:

import java.util.HashMap;
                                            //前面写了泛型,后面就不用写了
HashMap<String,Object> student=new HashMap();    //value的类型各种各样,处于兼容性需要,用远古父类Object
student.put("name","张三");        
String name=(String)student.put("name","李四");    //因为put方法的返回值是被覆盖的value,是Object类型,所以要强制类型转换
System.out.println(name+"已被替换为李四");

student.put("age",18);
student.put("height",182);
student.put("weight",60);
System.out.println(student);
System.out.println("当前kv的数量:"+student.size());

2.HashMap的案例演示:

public class HashMapSample {
    public static void main(String[] args) {
        //HashMap<String,Object> student=new HashMap<String,Object>();//标准写法,麻烦
        //HashMap<String,Object> student=new HashMap<>();     //泛型可以只写左边,右边可以省略
        HashMap<String,Object> student=new HashMap();          // 更简洁、易懂的写法
        
        //1.通过put方法,向Map中放入键值对
        student.put("name", "张三");
        //多次为同一个Key赋值,新的Value会覆盖旧Value,同时将旧Value返回
        String name = (String) student.put("name", "李四");   //先进行赋值修改,再返回
        System.out.println(name+"已被替换为李四");
        //Map可以存储多组键值对,且Value可以是不同类型
        student.put("age",18);   //虽然这里是整型,但是根据<String,Object>,键值对都是对象形式。即基本数据类型,会自动变为包装类
        student.put("height",182);
        student.put("weight",60);

        System.out.println(student);

        //2.使用get方法,获取指定Key的Value
        String n = (String) student.get("name");              //纯粹的进行查询
        System.out.println(n);

        //3.是否包含该Key?
        boolean k1 = student.containsKey("name");
        System.out.println(k1);
        boolean k2 = student.containsKey("name2");
        System.out.println(k2);

        //是否包含该value?
        boolean v1 = student.containsValue(60);
        System.out.println(v1);
        boolean v2 = student.containsValue(61);
        System.out.println(v2);

        //4.当前键值对的总数是?
        int count = student.size();
        System.out.println(count);

        //5.不想要某个key了?
        Integer w = (Integer) student.remove("weight");
        System.out.println("weight项已被移除,其value值为:"+w);
        System.out.println(student);                //将整个HashMap,打印出来
    }
}

--->
张三已被替换为李四
{name=李四, weight=60, age=18, height=182}        //无序

李四
true
false
true
false
    
4
    
weight项已被移除,其value值为:60
{name=李四, age=18, height=182}

3.LinkedMap的案例演示:

与上面标准的HashMap使用,基本一样。只是增加对于链表特性的实现。以保证提取数据显示的顺序与放入的顺序保持一致。

  • 查看下源码:

  • 代码
public class LinkedHashMapSample {
    public static void main(String[] args) {
        LinkedHashMap<String,Object> student=new LinkedHashMap<>();

        student.put("name", "张三");
        student.put("age",18);      
        student.put("height",182);
        student.put("weight",60);
        System.out.println(student);

    }
}

--->
{name=张三, age=18, height=182, weight=60}        //提取数据显示的顺序与放入的循序保持一致

4.实际开发应用中,在多个子接口引用指向实现类时,左边往往会简化为只写父类接口:

不管是HashMap,还是LinkedHashMap:

体现多态性:

public class LinkedHashMapSample {
    public static void main(String[] args) {
       // LinkedHashMap<String,Object> student=new LinkedHashMap<>();
        Map<String,Object> student=new LinkedHashMap<>();               //左边简化为使用父类接口Map

4.3 Map实现类:初识TreeMap

1.TreeMap特点:

  • TreeMap在存储Key-Value时,可以根据Key节点进行排序
  • TreeMap支持两种排序:自然排序、定制排序
  • 与TreeSet相同,TreeMap也是基于红黑树结构,对数据进行排序

其中,自然排序就是按照数字或字母顺序,升序排序

2.案例演示

  • 默认的自然排序

    public class TreeMapSample {
        public static void main(String[] args){
            Map<String,Object> record=new TreeMap<>();
            record.put("A1", "1");
            record.put("C3", "2");
            record.put("B5", "3");
            record.put("X1", "4");
            record.put("C1", "5");
            record.put("B1", "6");
            System.out.println(record);
        }
    }
    --->
    {A1=1, B1=6, B5=3, C1=5, C3=2, X1=4}        //默认的自然排序
  • 成功实现了降序排列
public class TreeMapSample {
    
    //新定义一个内部类,实现Comparator接口,以重写比较规则
    class RecordComparator implements Comparator<String> {//这里是String,因为比较的是Map<String,Object> record中的Key类型
        @Override
        public int compare(String o1, String o2) {
            return o2.compareTo(o1);        //因为比较双方是字符串,所以使用compareTo方法        //降序排列
        }
    }

    //新定义一个实例方法,将添加元素和打印元素的逻辑封装进去
    public void sort(){
        Map<String,Object> record=new TreeMap(new RecordComparator());//将对Key降序排列的内部类对象,放进构造方法中
        record.put("A1", "1");
        record.put("C3", "2");
        record.put("B5", "3");
        record.put("X1", "4");
        record.put("C1", "5");
        record.put("B1", "6");
        System.out.println(record);
    }

    public static void main(String[] args) {
        TreeMapSample sample=new TreeMapSample();
        sample.sort();
    }
}

--->
{X1=4, C3=2, C1=5, B5=3, B1=6, A1=1}        //成功实现了降序排列

4.4 Map集合的三种遍历方式

HashMap----------------乱序

LinkedHashMap--------按照插入顺序

TreeMap------------------自定义顺序

将集合中的数据全部一个一个打印出来,而不是输出整个集合形式:

与List遍历方式相同,也有三种遍历方式。

1.增强型for循环

public class LoopSample {

    //新定义一个实例方法,将for循环遍历的逻辑封装进去
    public void doForLoop(Map map){                //将map对象传入
        Set<String> keys = map.keySet();        //通过内置的keySet方法,得到Key的Set集合
        for(String k:keys){
            System.out.println(k+":"+map.get(k));
        }
    }

    public static void main(String[] args) {
        Map<String,Object> student=new LinkedHashMap<>();    //student:是包含很多键值对的Map映射集合的对象

        student.put("name", "张三");
        student.put("age",18);
        student.put("height",182);
        student.put("weight",60);
        System.out.println(student);

        LoopSample loopSample = new LoopSample();
        loopSample.doForLoop(student);
    }
}

--->
{name=张三, age=18, height=182, weight=60}

name:张三
age:18
height:182
weight:60

2.forEach方法遍历

利用对象自带的forEach方法+配合基于JDK1.8中的lambda表达式

写法简洁,可读性好,用的最多。

public class LoopSample {

    //新定义一个实例方法,将forEach遍历的逻辑封装进去
    public void doForEach(Map map){
        map.forEach((key,value)->{
            System.out.println(key+":"+value);
        });
    }

    public static void main(String[] args) {
        Map<String,Object> student=new LinkedHashMap<>();    //student:是包含很多键值对的Map映射集合的对象

        student.put("name", "张三");
        student.put("age",18);
        student.put("height",182);
        student.put("weight",60);
        System.out.println(student);

        LoopSample loopSample = new LoopSample();
        loopSample.doForEach(student);
    }
}
--->
{name=张三, age=18, height=182, weight=60}

name:张三
age:18
height:182
weight:60

3.Iterator迭代器对象遍历

最麻烦,早期版本多。

public class LoopSample {

    //新定义一个实例方法,将Iterator迭代器对象遍历的逻辑封装进去
   public void doIterator(Map map){
        Iterator<Map.Entry<String, Object>> itr = map.entrySet().iterator();      //每一组键值对,称为entry对象
        while(itr.hasNext()){
            Map.Entry<String, Object> entry = itr.next();
            System.out.println(entry.getKey()+":"+entry.getValue());
        }
    }

    public static void main(String[] args) {
        Map<String,Object> student=new LinkedHashMap<>();//student:是包含很多键值对的Map映射集合的对象
        
        student.put("name", "张三");
        student.put("age",18);
        student.put("height",182);
        student.put("weight",60);
        System.out.println(student);

        LoopSample loopSample = new LoopSample();
        loopSample.doIterator(student);
    }
}

--->
name:张三
age:18
height:182
weight:60

五、应用类Collections实现集合排序

5.1 类Collections实现集合排序

  • 已有的集合List
public class ListSorter {

    public static void main(String[] args) {
        List<Integer> list=new ArrayList();
        list.add(70);
        list.add(90);
        list.add(30);
        list.add(50);
        System.out.println(list);
    }
}

--->
[70, 90, 30, 50]
  • 通过Collections.sort()方法,可以对已有的List进行重排序
public class ListSorter {

    //定义一个实例方法,用于对list集合进行排序
    public List<Integer> sort(List<Integer> list){
        Collections.sort(list);        //是对list内部进行加工排列,并不会产生新的list
        System.out.println(list);
        return list;                //这个list就是传入的参数list
    }

    public static void main(String[] args) {
        List<Integer> list=new ArrayList<>();
        list.add(70);
        list.add(90);
        list.add(30);
        list.add(50);
        System.out.println(list);

        ListSorter listSorter=new ListSorter();
        List<Integer> list1 = listSorter.sort(list);    //调用实例方法
        System.out.println(list);
        System.out.println(list1);

    }
}

--->
[70, 90, 30, 50]
[30, 50, 70, 90]
[30, 50, 70, 90]        //原始的list
[30, 50, 70, 90]        //现在的list,与上述原始的list一样。

这说明,工具类Collections.sort()方法只是对原始的集合进的内部调整,并不会产生新的list

  • Comparator接口的实现自定义比较的逻辑
public class ListSorter {
    
    //新定义一个内部类,用于包含比较集合的逻辑
    class SampleComparator implements Comparator<Integer>{
        //70  90  30  50
        //结果>0,则交换位置‘
        //结果=0或<0,则位置不变
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2-o1;                    //降序排列
        }
    }

    //在实例方法中,将上述包含比较逻辑的内部类的对象,传进来
    public List<Integer> sort(List<Integer> list){
        Collections.sort(list,new SampleComparator());        //传入比较器
        System.out.println(list);
        return list;
    }

    public static void main(String[] args) {
        List<Integer> list=new ArrayList<>();
        list.add(70);
        list.add(90);
        list.add(30);
        list.add(50);
        System.out.println(list);

        ListSorter listSorter=new ListSorter();
        List<Integer> list1 = listSorter.sort(list);
        System.out.println(list);
        System.out.println(list1);

    }
}

--->
[70, 90, 30, 50]
[90, 70, 50, 30]        //成功实现了降序排列
[90, 70, 50, 30]
[90, 70, 50, 30]

5.2 自定义类的集合排序

上述是基于基础数据类型的排序,如果遇到自定义类的集合呢?

(1)实现的接口Comparator后面的泛型,必须是自定义类

(2)指定排序规则时,需要先获得具体的属性,然后对属性进行比较

//自定义的商品类
public class Goods {
    private String sn;      //商品的条码系列号
    private String title;   //商品的标题

    public Goods(String sn,String title){
        this.sn=sn;
        this.title=title;
    }

    //重写了toString方法,方便程序调试
    @Override
    public String toString(){
        return "Goods{"+"sn='"+sn+'\''+",title='"+title+'\''+'}';
    }

    public String getSn(){
        return sn;
    }

    public String getTitle(){return title;}
}
public class CustomObjectSortSample {

    //新定义一个内部类,实现父类接口,重写父类接口的比较规则
    private class CustomComparator implements Comparator<Goods>{        // (1)
        @Override
        public int compare(Goods o1, Goods o2) {
            return o1.getSn().compareTo(o2.getSn());        // (2)
            //return o1.getTitle().compareTo(o2.getTitle());    如果按照商品的题目进行排序,就调用题目的get方法,获得属性值
        }
    }

    //新定义一个实例方法,将比较逻辑封装进去
    public List<Goods> sort(List<Goods> list) {
        CustomComparator comp=new CustomComparator();
        Collections.sort(list, comp);   //将集合对象、比较器对象,一起传参进去
        return list;
    }

    public static void main(String[] args) {
        List<Goods> goodList=new ArrayList<>(); //注意:新定义的集合,其元素类型是自定义的类Goods的对象

        goodList.add(new Goods("18827382190", "商品A"));
        goodList.add(new Goods("83219078978", "商品B"));
        goodList.add(new Goods("83241781237", "商品C"));
        goodList.add(new Goods("18579831426", "商品D"));

        System.out.println("排序前:");
        System.out.println(goodList);
        System.out.println("排序后:");

        List<Goods> list = new CustomObjectSortSample().sort(goodList);
        System.out.println(list);
    }
}

--->
排序前:
[Goods{sn='18827382190',title='商品A'}, Goods{sn='83219078978',title='商品B'}, Goods{sn='83241781237',title='商品C'}, Goods{sn='18579831426',title='商品D'}]
排序后:
[Goods{sn='18579831426',title='商品D'}, Goods{sn='18827382190',title='商品A'}, Goods{sn='83219078978',title='商品B'}, Goods{sn='83241781237',title='商品C'}]

声明:Jerry's Blog|版权所有,违者必究|如未注明,均为原创|本网站采用BY-NC-SA协议进行授权

转载:转载请注明原文链接 - 6.1 常见集合与应用


Follow excellence, and success will chase you.