爆款云主机2核4G限时秒杀,88元/年起!
查看详情

活动

天翼云最新优惠活动,涵盖免费试用,产品折扣等,助您降本增效!
热门活动
  • 618智算钜惠季 爆款云主机2核4G限时秒杀,88元/年起!
  • 免费体验DeepSeek,上天翼云息壤 NEW 新老用户均可免费体验2500万Tokens,限时两周
  • 云上钜惠 HOT 爆款云主机全场特惠,更有万元锦鲤券等你来领!
  • 算力套餐 HOT 让算力触手可及
  • 天翼云脑AOne NEW 连接、保护、办公,All-in-One!
  • 中小企业应用上云专场 产品组合下单即享折上9折起,助力企业快速上云
  • 息壤高校钜惠活动 NEW 天翼云息壤杯高校AI大赛,数款产品享受线上订购超值特惠
  • 天翼云电脑专场 HOT 移动办公新选择,爆款4核8G畅享1年3.5折起,快来抢购!
  • 天翼云奖励推广计划 加入成为云推官,推荐新用户注册下单得现金奖励
免费活动
  • 免费试用中心 HOT 多款云产品免费试用,快来开启云上之旅
  • 天翼云用户体验官 NEW 您的洞察,重塑科技边界

智算服务

打造统一的产品能力,实现算网调度、训练推理、技术架构、资源管理一体化智算服务
智算云(DeepSeek专区)
科研助手
  • 算力商城
  • 应用商城
  • 开发机
  • 并行计算
算力互联调度平台
  • 应用市场
  • 算力市场
  • 算力调度推荐
一站式智算服务平台
  • 模型广场
  • 体验中心
  • 服务接入
智算一体机
  • 智算一体机
大模型
  • DeepSeek-R1-昇腾版(671B)
  • DeepSeek-R1-英伟达版(671B)
  • DeepSeek-V3-昇腾版(671B)
  • DeepSeek-R1-Distill-Llama-70B
  • DeepSeek-R1-Distill-Qwen-32B
  • Qwen2-72B-Instruct
  • StableDiffusion-V2.1
  • TeleChat-12B

应用商城

天翼云精选行业优秀合作伙伴及千余款商品,提供一站式云上应用服务
进入甄选商城进入云市场创新解决方案
办公协同
  • WPS云文档
  • 安全邮箱
  • EMM手机管家
  • 智能商业平台
财务管理
  • 工资条
  • 税务风控云
企业应用
  • 翼信息化运维服务
  • 翼视频云归档解决方案
工业能源
  • 智慧工厂_生产流程管理解决方案
  • 智慧工地
建站工具
  • SSL证书
  • 新域名服务
网络工具
  • 翼云加速
灾备迁移
  • 云管家2.0
  • 翼备份
资源管理
  • 全栈混合云敏捷版(软件)
  • 全栈混合云敏捷版(一体机)
行业应用
  • 翼电子教室
  • 翼智慧显示一体化解决方案

合作伙伴

天翼云携手合作伙伴,共创云上生态,合作共赢
天翼云生态合作中心
  • 天翼云生态合作中心
天翼云渠道合作伙伴
  • 天翼云代理渠道合作伙伴
天翼云服务合作伙伴
  • 天翼云集成商交付能力认证
天翼云应用合作伙伴
  • 天翼云云市场合作伙伴
  • 天翼云甄选商城合作伙伴
天翼云技术合作伙伴
  • 天翼云OpenAPI中心
  • 天翼云EasyCoding平台
天翼云培训认证
  • 天翼云学堂
  • 天翼云市场商学院
天翼云合作计划
  • 云汇计划
天翼云东升计划
  • 适配中心
  • 东升计划
  • 适配互认证

开发者

开发者相关功能入口汇聚
技术社区
  • 专栏文章
  • 互动问答
  • 技术视频
资源与工具
  • OpenAPI中心
开放能力
  • EasyCoding敏捷开发平台
培训与认证
  • 天翼云学堂
  • 天翼云认证
魔乐社区
  • 魔乐社区

支持与服务

为您提供全方位支持与服务,全流程技术保障,助您轻松上云,安全无忧
文档与工具
  • 文档中心
  • 新手上云
  • 自助服务
  • OpenAPI中心
定价
  • 价格计算器
  • 定价策略
基础服务
  • 售前咨询
  • 在线支持
  • 在线支持
  • 工单服务
  • 建议与反馈
  • 用户体验官
  • 服务保障
  • 客户公告
  • 会员中心
增值服务
  • 红心服务
  • 首保服务
  • 客户支持计划
  • 专家技术服务
  • 备案管家

了解天翼云

天翼云秉承央企使命,致力于成为数字经济主力军,投身科技强国伟大事业,为用户提供安全、普惠云服务
品牌介绍
  • 关于天翼云
  • 智算云
  • 天翼云4.0
  • 新闻资讯
  • 天翼云APP
基础设施
  • 全球基础设施
  • 信任中心
最佳实践
  • 精选案例
  • 超级探访
  • 云杂志
  • 分析师和白皮书
  • 天翼云·创新直播间
市场活动
  • 2025智能云生态大会
  • 2024智算云生态大会
  • 2023云生态大会
  • 2022云生态大会
  • 天翼云中国行
天翼云
  • 活动
  • 智算服务
  • 产品
  • 解决方案
  • 应用商城
  • 合作伙伴
  • 开发者
  • 支持与服务
  • 了解天翼云
      • 文档
      • 控制中心
      • 备案
      • 管理中心

      Java基础深化和提高-------容器篇

      首页 知识中心 软件开发 文章详情页

      Java基础深化和提高-------容器篇

      2023-08-04 08:33:17 阅读次数:430

      java,容器

      一、泛型(Generics)

      Java基础深化和提高-------容器篇

       为了能够更好的学习容器,我们首先要先来学习一个概念:泛型。
      泛型基本概念,泛型是JDK5.0以后增加的新特性。
      泛型的本质就是“数据类型的参数化”,处理的数据类型不是固定的,而是可以作为参数传入。我们可以把“泛型”理解为数据类型的一个占位符(类似:形式参数),即告诉编译器,在调用泛型时必须传入实际类型.

      参数化类型,白话说就是:
        1.把类型当作是参数一样传递。
      2. <数据类型> 只能是引用类型。

      泛型的好处

      在不使用泛型的情况下,我们可以使用Object类型来实现任意的参数类型,但是在使用时需要我们强制进行类型转换。这就要求程序员明确知道实际类型,不然可能引起类型转换错误;但是,在编译期我们无法识别这种错误,只能在运行期发现这种错误。使用泛型的好处就是可以在编译期就识别出这种错误,有了更好的安全性;同时,所有类型转换由编译器完成,在程序员看来都是自动转换的,提高了代码的可读性。

      总结一下,就是使用泛型主要是两个好处:
      1、代码可读性更好【不用强制转换】
      2、程序更加安全【只要编译时期没有警告,运行时期就不会出现ClassCastException异常】

       类型擦除

      编码时采用泛型写的类型参数,编译器会在编译时去掉,这称之为“类型擦除”。
      泛型主要用于编译阶段,编译后生成的字节码class文件不包含泛型中的类型信息,涉及类型转换仍然是普通的强制类型转换。类型参数在编译后会被替换成Object,运行时虚拟机并不知道泛型。
      泛型主要是方便了程序员的代码编写,以及更好的安全性检测。

       实时效果反馈
      1.泛型是在JDK哪个版本中增加的新特性?
      A  JDK5.0
      B JDK6.0
      C JDK7.0
      D JDK8.0

       

      2.泛型的本质是什么?
      A 数据的参数化
      B 对象的参数化
      C 数据类型的参数化
      D 基本类型的参数化

       

      答案
      1=>A 2=>C

      泛型类

      Java基础深化和提高-------容器篇

       泛型标记


      定义泛型时,一般采用几个标记:E、T、K、V、N、?。他们约定
      俗称的含义如下:

      泛型标记  对应单词  说明
      E Element 在容器中使用,表示容器中的元素
      T Type 表示普通的JAVA类
      K Key 表示键,例如:Map中的键Key
      V Value 表示值
      N Number 表示数值类型
      ?     表示不确定的JAVA类型

      泛型类的使用

       语法结构

      public class 类名<泛型标识符号> {  }
      
      public class 类名<泛型标识符号,泛型标识符号> {  }

       示例

      public class Generic<T> {
        private T  flag;
        public void setFlag(T flag){
          this.flag = flag;
       }
        public T getFlag(){
          return this.flag;
       }
      }
      public class Test {
        public static void main(String[] args) {
      
          //创建对象时,指定泛型具体类型。
          Generic<String> generic = new Generic<>();
      
          generic.setFlag("admin");
      
          String flag = generic.getFlag();
          System.out.println(flag);
         
          //创建对象时,指定泛型具体类型。
          Generic<Integer> generic1 = new Generic<>();
      
          generic1.setFlag(100);
      
          Integer flag1 = generic1.getFlag();
          System.out.println(flag1);
       }
      }

      实时效果反馈

      1.如下哪个选项是正确定义泛型类的语法
      A public class <泛型标识符号> 类名
      B public <泛型标识符号> class 类名
      C <泛型标识符号> public class 类名
      D public class 类名<泛型标识符号>

       

      答案
      1=>D

       泛型接口

      Java基础深化和提高-------容器篇

       泛型接口和泛型类的声明方式一致。

      泛型接口的使用

      语法结构

      public interface 接口名<泛型标识符号> {   }
      public interface 接口名<泛型标识符号,泛型标识符号>{   }

       示例

      public interface IGeneric<T> {
        T getName(T name);
      }
      
      
      
      //在实现接口时传递具体数据类型
      public class IgenericImpl implements
      Igeneric<String> {
        @Override
        public String getName(String name) {
          return name;
       }
      }
      
      
      //在实现接口时仍然使用泛型作为数据类型
      public class IGenericImpl2<T> implements
      IGeneric<T>{
        @Override
        public T getName(T name) {
          return name;
       }
      }
      
      public class Test {
        public static void main(String[] args) {
          IGeneric<String> igeneric= new  IGenericImpl();
          String name = igeneric.getName("old");
          System.out.println(name);
          IGeneric<String> igeneric1 = new IGenericImpl2<>();
          String name1 = igeneric1.getName("it");
          System.out.println(name1);
       }
      }

      实时效果反馈
      1.如下哪个选项是正确定义泛型接口的语法
      A public interface<泛型标识符号> 接口名
      B public <泛型标识符号> interface 接口名
      C <泛型标识符号> public interface 接口名
      D public interface 接口名<泛型标识符号>

       

      答案
      1=>D

      泛型方法 

      Java基础深化和提高-------容器篇

       类上定义的泛型,在方法中也可以使用。但是,我们经常需要仅仅在某一个方法上使用泛型,这时候可以使用泛型方法。
      调用泛型方法时,不需要像泛型类那样告诉编译器是什么类型,编译器可以自动推断出类型

       泛型方法的使用


       非静态方法

      非静态方法可以使用泛型类中所定义的泛型,也可以将泛型定义在方法上。

       语法结构

      //无返回值方法
      public <泛型标识符号> void getName(泛型标识符号name) {  }
      
      //有返回值方法
      public <泛型标识符号> 泛型标识符号 getName(泛型标识符号 name) {  }

       示例

      public class MethodGeneric {
        public <T> void setName(T name){
          System.out.println(name);
       }
        public <T> T getAge(T age){
          return age;
       }
      }
      public class Test2 {
        public static void main(String[] args) {
      
          MethodGeneric methodGeneric = new MethodGeneric();
      
          methodGeneric.setName("old");
      
          Integer age = methodGeneric.getAge(123);
      
          System.out.println(age);
       }

      静态方法

      静态方法中使用泛型时有一种情况需要注意一下,那就是静态方法无法访问类上定义的泛型,所以必须要将泛型定义在方法上。

      语法结构

      //无返回值静态方法
      public static <泛型标识符号> void setName(泛型标识符号 name){  }
      
      //有返回值静态方法
      public static <泛型标识符号> 泛型表示符号getName(泛型标识符号 name){  }

      示例

      public class MethodGeneric {
        public static <T> void setFlag(T flag){
          System.out.println(flag);
       }
        public static <T> T getFlag(T flag){
          return flag;
       }
      }
      public class Test4 {
        public static void main(String[] args) {
      
          MethodGeneric.setFlag("old");
      
          Integer flag1 =  MethodGeneric.getFlag(123123);
      
          System.out.println(flag1);
       }
      }

      实时效果反馈
      1.如下哪个选项是正确定义泛型方法的语法
      A <泛型标识符号> public void getName(泛型标识符号 name)
      B public void <泛型标识符号> getName(泛型标识符号 name)
      C public <泛型标识符号> void getName(泛型标识符号 name)
      D public void getName <泛型标识符号>(泛型标识符号 name)

       

      答案
      1=>C

      泛型方法与可变参数 

      Java基础深化和提高-------容器篇

       在泛型方法中,泛型也可以定义可变参数类型。

      语法结构

      public <泛型标识符号> void showMsg(泛型标识符号... agrs){  }

      示例

      public class MethodGeneric {
        public <T> void method(T...args){
      
          for(T t:args){
            System.out.println(t);
           }
      
         }
      }
      
      public class Test5 {
        public static void main(String[] args) {
          MethodGeneric methodGeneric = new  MethodGeneric();
      
          String[] arr = new String[]{"a","b","c"};
      
          Integer[] arr2 = new Integer[]{1,2,3};
      
          methodGeneric.method(arr);
      
          methodGeneric.method(arr2);
        }
      }

      实时效果反馈
      1.如下哪个选项是正确的在可变参数中使用泛型
      A public <泛型标识符号> void showMsg(泛型标识符号... agrs)
      B public void showMsg(<泛型标识符号>... agrs)
      C public <泛型标识符号> void showMsg(<泛型标识符号>... agrs)

      D public <泛型标识符号> void showMsg(Object... agrs)


      答案
      1=>A

       泛型中的通配符

      Java基础深化和提高-------容器篇

       无界通配符
      “?”表示类型通配符,用于代替具体的类型。它只能在“<>”中使用。可以解决当具体类型不确定的问题。

      语法结构

      public void showFlag(Generic<?> generic){  }

      示例

      public class Generic<T> {
      
        private T  flag;
      
        public void setFlag(T flag){
          this.flag = flag;
       }
      
        public T getFlag(){
          return this.flag;
        }
      }
      
      
      public class ShowMsg {
        public void showFlag(Generic<?> generic){
        
        System.out.println(generic.getFlag());
       }
      }
      
      
      public class Test3 {
        public static void main(String[] args) {
      
          ShowMsg showMsg = new ShowMsg();
          Generic<Integer> generic = new  Generic<>();
          generic.setFlag(20);
          showMsg.showFlag(generic);
      
          Generic<Number> generic1 = new  Generic<>();
          generic1.setFlag(50);
          showMsg.showFlag(generic1);
      
          Generic<String> generic2 = new Generic<>();
          generic2.setFlag("old");
          showMsg.showFlag(generic2);
       }
      }
      

      实时效果反馈
      1.在泛型中,无界通配符使用什么符号来表示?
      A !
      B ?
      C #
      D *

       

      答案
      1=>B

       统配符的上下限定
       统配符的上限限定

      Java基础深化和提高-------容器篇

       对通配符的上限的限定:<? extends 类型>  ?实际类型可以是上限限定中所约定的类型,也可以是约定类型的子类型;

      语法结构

      public void showFlag(Generic<? extends Number> generic){ }

      示例

      public class ShowMsg {
      
        public void showFlag(Generic<? extends Number> generic){
      
              System.out.println(generic.getFlag());
        }
      }
      
      
      public class Test4 {
      
        public static void main(String[] args) {
      
          ShowMsg showMsg = new ShowMsg();
          Generic<Integer> generic = new  Generic<>();
          generic.setFlag(20);
      
          showMsg.showFlag(generic);
          Generic<Number> generic1 = new  Generic<>();
      
          generic1.setFlag(50);
          showMsg.showFlag(generic1);
       }
      }

      实时效果反馈
      1.对通配符的上限的限定是指
      A 实际类型只能是上限限定中所约定的类型;
      B 实际类型只能是上限限定中所约定类型的子类型;

      C 实际类型可以是上限限定中所约定的类型,也可以是约定类型的子类型;
      D 实际类型可以是上限限定中所约定的类型,也可以是约定类型的父类型;

       

      答案
      1=>C

       通配符的下限限定

      Java基础深化和提高-------容器篇

       对通配符的下限的限定:<? super 类型>  ?实际类型可以是下限限定中所约定的类型,也可以是约定类型的父类型;

      语法结构

      public void showFlag(Generic<? super Integer> generic){ }

      示例

      public class ShowMsg {
      
        public void showFlag(Generic<? super Integer> generic){
        
           System.out.println(generic.getFlag());
         }
      }
      
      
      public class Test6 {
      
        public static void main(String[] args) {
      
          ShowMsg showMsg = new ShowMsg();
          Generic<Integer> generic = new Generic<>();
          generic.setFlag(20);
          showMsg.showFlag(generic);
      
          Generic<Number> generic1 = new Generic<>();
          generic1.setFlag(50);
          showMsg.showFlag(generic1);
       }
      }

      实时效果反馈

      1.对通配符的下限的限定是指
      A 实际类型只能是下限限定中所约定的类型;
      B 实际类型只能是下限限定中所约定类型的子类型;
      C 实际类型可以是下限限定中所约定的类型,也可以是约定类型的子类型;
      D 实际类型可以是下限限定中所约定的类型,也可以是约定类型的父类型;

       

      答案

      1=>D

      泛型局限性和常见错误

      Java基础深化和提高-------容器篇

       泛型主要用于编译阶段,编译后生成的字节码class文件不包含泛型中的类型信息。 类型参数在编译后会被替换成Object,运行时虚拟机并不知道泛型。因此,使用泛型时,如下几种情况是错的:

      Java基础深化和提高-------容器篇

       实时效果反馈 


      1.如下哪个选项是错误的使用泛型?
      A Generic
      B Generic
      C Generic
      D Generic

       

      答案
      1=>D

      二、容器介绍

      容器简介

      容器,是用来容纳物体、管理物体。生活中,我们会用到各种各样的容器。如锅碗瓢盆、箱子和包等。

      Java基础深化和提高-------容器篇

       程序中的“容器”也有类似的功能,用来容纳和管理数据。比如,如下新闻网站的新闻列表、教育网站的课程列表就是用“容器”来管理:

      开发和学习中需要时刻和数据打交道,如何组织这些数据是我们编程中重要的内容。 我们一般通过“容器”来容纳和管理数据。事实上,我们前面所学的数组就是一种容器,可以在其中
      放置对象或基本类型数据。
      数组的优势:是一种简单的线性序列,可以快速地访问数组元素,效率高。如果从查询效率和类型检查的角度讲,数组是最好的。
      数组的劣势:不灵活。容量需要事先定义好,不能随着需求的变化而扩容。比如:我们在一个用户管理系统中,要把今天注册的所有用户取出来,那么这样的用户有多少个?我们在写程序时是无法确定的。因此,在这里就不能使用数组。

       基于数组并不能满足我们对于“管理和组织数据的需求”,所以我们需要一种更强大、更灵活、容量随时可扩的容器来装载我们的对象。 这就是我们今天要学习的容器,也叫集合(Collection)。

       实时效果反馈

      1.Java中容器的作用是什么?
      A 容纳数据
      B 处理数据
      C 生产数据
      D 销毁数据

       

      答案
      1=>A

       容器的结构

      结构图

      Java基础深化和提高-------容器篇

       单例集合

      Java基础深化和提高-------容器篇

       双例集合

      Java基础深化和提高-------容器篇

       实时效果反馈

      1.如下哪个接口不是容器接口?
      A List
      B Set
      C Map
      D Comparable

       

      答案
      1=>D

       单例集合


      Collection接口介绍

      Collection 表示一组对象,它是集中、收集的意思。Collection接口的两个子接口是List、Set接口。

       Java基础深化和提高-------容器篇

       Collection接口中定义的方法

      方法  说明
      boolean add(Object element) 增加元素到容器中
      boolean remove(Object element) 从容器中移除元素
      boolean contains(Object element) 容器中是否包含该元素
      int size() 容器中元素的数量
      boolean isEmpty() 容器是否为空
      void clear() 清空容器中所有元素
      Iterator iterator() 获得迭代器,用于遍历所有元素
      boolean containsAll(Collection c) 本容器是否包含c容器中的所有元素
      boolean addAll(Collection c) 将容器c中所有元素增加到本容器
      boolean removeAll(Collection c) 移除本容器和容器c中都包含的元素
      boolean retainAll(Collection c) 取本容器和容器c中都包含的元素,移除非交集元素
      Object[] toArray() 转化成Object数组

      由于List、Set是Collection的子接口,意味着所有List、Set的实现类都有上面的方法。

      JDK8之后,Collection接口新增的方法(将在JDK新特性和函数式编程中介绍):

      新增方法  说明
      removeIf 作用是删除容器中所有满足filter指定条件的元素
      stream
      parallelStream
      stream和parallelStream 分别返回该容器的Stream视图表示,不同之处在于parallelStream()返回并行的Stream,Stream是Java函数式编程的核心类
      spliterator 可分割的迭代器,不同以往的iterator需要顺序迭代,Spliterator可以分割为若干个小的迭代器进行并行操作,可以实现多线程操作提高效率

       

      实时效果反馈 

      1.如下哪个接口不是Collection接口的子接口?
      A List
      B Set
      C Map
      D Queue

       

      答案
      1=>C

      List接口介绍

      Java基础深化和提高-------容器篇

       List接口特点

      List是有序、可重复的容器。

      有序:有序(元素存入集合的顺序和取出的顺序一致)。List中每个元 素都有索引标记。可以根据元素的索引标记(在List中的位置)访问 元素,从而精确控制这些元素。

      可重复:List允许加入重复的元素。更确切地讲,List通常允许满足 e1.equals(e2) 的元素重复加入容器。

      List接口中的常用方法

      除了Collection接口中的方法,List多了一些跟顺序(索引)有关的方 法,参见下表:

       Java基础深化和提高-------容器篇

       ArrayList容器的基本使用

      Java基础深化和提高-------容器篇

       ArrayList是List接口的实现类。是List存储特征的具体实现。 ArrayList底层是用数组实现的存储。 特点:查询效率高,增删效率 低,线程不安全。

      public class ArrayListTest {
          public static void main(String[] args) {
              //实例化ArrayList容器
              List<String> list  = new ArrayList<>();
              //添加元素
              boolean flag1 = list.add("oldlu");
              boolean flag2 = list.add("itbz");
              boolean flag3 = list.add("sxt");
              boolean flag4 = list.add("sxt");
              System.out.println(flag1+"\t"+flag2+"\t"+flag3+"\t"+flag4);
              //删除元素
              boolean flag4 = list.remove("oldlu");
              System.out.println(flag4);
              //获取容器中元素的个数
              int size = list.size();
              System.out.println(size);
              //判断容器是否为空
              boolean empty = list.isEmpty();
              System.out.println(empty);
              //容器中是否包含指定的元素
              boolean value = list.contains("itbz");
              System.out.println(value);
              
              
              //清空容器
              list.clear();
              Object[] objects1 = list.toArray();
              System.out.println(Arrays.toString(objects1));
         }
      }

      ArrayList容器的索引操作

      Java基础深化和提高-------容器篇

      public class ArrayListTest2 {
          public static void main(String[] args) {
              //实例化容器
              List<String> list = new ArrayList<>();
      
              //添加元素
              list.add("oldlu");
              list.add("itbz");
      
              //向指定位置添加元素
              list.add(0,"sxt");
              System.out.println("获取元素");
              String value1 = list.get(0);
              System.out.println(value1);
              System.out.println("获取所有元素方式一");
      
              //使用普通for循环
              for(int i=0;i<list.size();i++){
                  System.out.println(list.get(i));
               }
              System.out.println("获取所有元素方式二");
      
              //使用Foreach循环
              for(String str:list){
                  System.out.println(str);
             }
             System.out.println("元素替换");
              list.set(1,"kevin");
              for(String str:list){
                  System.out.println(str);
             }
              System.out.println("根据索引位置删除元素);
              String value2 = list.remove(1);
              System.out.println(value2);
              System.out.println("----------------");
              for(String str:list){
                  System.out.println(str);
             }
              System.out.println("查找元素第一次出现的位置");
              int value3 = list.indexOf("sxt");
              System.out.println(value3);
              System.out.println("查找元素最后一次出现的位置");
              list.add("sxt");
              for(String str:list){
                  System.out.println(str);
             }
              int value4 = list.lastIndexOf("sxt");
              System.out.println(value4);
         }
      }

       ArrayList的并集、交集、差集

      并集

        //并集操作:将另一个容器中的元素添加到当前容器中
              List<String> a  = new ArrayList<>();
              a.add("a");
              a.add("b");
              a.add("c");
      
              List<String> b = new ArrayList<>();
              b.add("a");
              b.add("b");
              b.add("c");
              //a并集b
              a.addAll(b);
              for(String str :a){
                  System.out.println(str);
             }

      交集

              //交集操作:保留相同的,删除不同的
              List<String> a1  = new ArrayList<>();
              a1.add("a");
              a1.add("b");
              a1.add("c");
      
              List<String> b1 = new ArrayList<>();
              b1.add("a");
              b1.add("d");
              b1.add("e");
              
              //交集操作
              a1.retainAll(b1);
              for(String str :a1){
                  System.out.println(str);
             }

      差集

               //差集操作:保留不同的,删除相同的
              List<String> a2  = new ArrayList<>();
              a2.add("a");
              a2.add("b");
              a2.add("c");
      
              List<String> b2= new ArrayList<>();
              b2.add("b");
              b2.add("c");
              b2.add("d");
              a2.removeAll(b2);
              for(String str :a2){
                  System.out.println(str);
             }

      ArrayList源码分析

      ArrayList底层是用数组实现的存储。

      成员变量

      /**
      * Default initial capacity.
      */
         private static final int DEFAULT_CAPACITY = 10;
      /**
      * The array buffer into which the elements of the ArrayList are stored.
      /**
      * The array buffer into which the elements of the ArrayList are stored.
      * The capacity of the ArrayList is the length of this array buffer. Any
      * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
      * will be expanded to DEFAULT_CAPACITY when the first element is added.
      */
      
      transient Object[] elementData; // nonprivate to simplify nested class access
      
      
      /**
      * The size of the ArrayList (the number of elements it contains).
      *
      * @serial
      */
      
      private int size;

      数组初始大小

      /**
      * Default initial capacity.
      */
      private static final int DEFAULT_CAPACITY = 10;

      添加元素

      /**
      * Appends the specified element to the end of this list.
      *
      * @param e element to be appended to this list
      * @return <tt>true</tt> (as specified by {@link Collection#add})
      */
      public boolean add(E e) {
          ensureCapacityInternal(size + 1);  //Increments modCount!!
          elementData[size++] = e;
          return true;
      }

      判断数组是否扩容

      //容量检查
      private void ensureCapacityInternal(int minCapacity) {  
          ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
      }
      //容量确认
      private void ensureExplicitCapacity(int minCapacity) {
          modCount++;
          //判断是否需要扩容,数组中的元素个数-数组长度,如果大于0表明需要扩容
          if (minCapacity - elementData.length > 0)
              grow(minCapacity);
      }

      数组扩容

      /**
      * Increases the capacity to ensure that it can hold at least the
      * number of elements specified by the minimum capacity argument.
      *
      * @param minCapacity the desired minimum capacity
      */
      private void grow(int minCapacity) {
        // overflow-conscious code
          int oldCapacity = elementData.length;
        //扩容1.5倍
          int newCapacity = oldCapacity + (oldCapacity >> 1);
          if (newCapacity - minCapacity < 0)
              newCapacity = minCapacity;
          if (newCapacity - MAX_ARRAY_SIZE > 0)
              newCapacity = hugeCapacity(minCapacity);
          // minCapacity is usually close to size, so this is a win:
          elementData = Arrays.copyOf(elementData,newCapacity);
      }

      Vector容器的基本使用

      Vector底层是用数组实现的,相关的方法都加了同步检查,因此“线 程安全,效率低”。 比如,indexOf方法就增加了synchronized同步 标记。

       Java基础深化和提高-------容器篇

       Vector的使用

      Vector的使用与ArrayList是相同的,因为他们都实现了List接口, 对List接口中的抽象方法做了具体实现。

      public class VectorTest {
          public static void main(String[] args) {
              //实例化Vector
              List<String> v = new Vector<>();
              v.add("a");
              v.add("b");
              v.add("a");
              for(int i=0;i<v.size();i++){
                  System.out.println(v.get(i));
             }
              System.out.println("----------------------");
              for(String str:v){
                  System.out.println(str);
             }
         }
      }

      Vector源码分析

      成员变量

      /**
      * The array buffer into which the components of the vector are
      * stored. The capacity of the vector is the length of this array buffer,
      * and is at least large enough to contain all the vector's elements.
      *
      * <p>Any array elements following the last element in the Vector are null.
      *
      * @serial
      */
      
      protected Object[] elementData;
      
          /**
           * The number of valid components in this {@code Vector} object.
           * Components {@code elementData[0]} through
           * {@code elementData[elementCount-1]} are the actual items.
           *
           * @serial
           */
      
      protected int elementCount;
          
          /**
           * The amount by which the capacity of the vector is automatically
           * incremented when its size becomes greater than its capacity. If
           * the capacity increment is less than or equal to zero, the capacity
           * of the vector is doubled each time it needs to grow.
           *
           * @serial
           */
      protected int capacityIncrement;

      构造方法

      public Vector() {
          this(10);
      }

      添加元素

      /**
      * Appends the specified element to the end of this Vector.
      *
      * @param e element to be appended to this Vector
      * @return {@code true} (as specified by {@link Collection#add})
      * @since 1.2
      */
      
      public synchronized boolean add(E e) {
          modCount++;
          ensureCapacityHelper(elementCount + 1);
          elementData[elementCount++] = e;
          return true;
      }

      数组扩容

      /**
      * This implements the unsynchronized semantics of ensureCapacity.
      * Synchronized methods in this class can internally call this
      * method for ensuring capacity without incurring the cost of an
      * extra synchronization.
      *
      * @see #ensureCapacity(int)
      */
      
      private void ensureCapacityHelper(int minCapacity) {
          // overflow-conscious code
         //判断是否需要扩容,数组中的元素个数-数组长度,如果大于0表明需要扩容
          
      if (minCapacity - elementData.length >0)
              grow(minCapacity);
      }
      private void grow(int minCapacity) {
          // overflow-conscious code
          int oldCapacity = elementData.length;
      
         //扩容2倍
          int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);
      
          if (newCapacity - minCapacity < 0)
              newCapacity = minCapacity;
          if (newCapacity - MAX_ARRAY_SIZE > 0)
              newCapacity = hugeCapacity(minCapacity);
          elementData = Arrays.copyOf(elementData, newCapacity);
      }

      LinkedList容器介绍

      LinkedList底层用双向链表实现的存储。特点:查询效率低,增删 效率高,线程不安全。 双向链表也叫双链表,是链表的一种,它的每个数据节点中都有两 个指针,分别指向前一个节点和后一个节点。 所以,从双向链表中 的任意一个节点开始,都可以很方便地找到所有节点。

       LinkedList的存储结构图

      Java基础深化和提高-------容器篇

       每个节点都应该有3部分内容:

      class  Node<E> {
          Node<E>  previous;   //前一个节点
          E  element;          //本节点保存的数据
          Node<E> next;        //后一个节点
      }

      List实现类的选用规则

      如何选用ArrayList、LinkedList、Vector?

      1 需要线程安全时,用Vector。

      2 不存在线程安全问题时,并且查找较多用ArrayList(一般使用它)

      3 不存在线程安全问题时,增加或删除元素较多用LinkedList

      LinkedList容器的使用(List标准) 

      LinkedList实现了List接口,所以LinkedList是具备List的存储特征的 (有序,元素有重复)。

      public class LinkedListTest {
          public static void main(String[] args) {
              //实例化LinkedList容器
              List<String> list = new LinkedList<>();
              //添加元素
              boolean a = list.add("a");
              boolean b = list.add("b");
              boolean c = list.add("c");
              list.add(3,"a");
              System.out.println(a+"\t"+b+"\t"+c);
              for(int i=0;i<list.size();i++){
                  System.out.println(list.get(i));
             }
         }
      }
      

      LinkedList容器的使用(非List标准)

      Java基础深化和提高-------容器篇

       

      public class LinkedListTest2 {
          public static void main(String[] args) {
              System.out.println("-------LinkedList-------------");
              //将指定元素插入到链表开头
              LinkedList<String> linkedList1 = new LinkedList<>();
              linkedList1.addFirst("a");
              linkedList1.addFirst("b");
              linkedList1.addFirst("c");
              for (String str:linkedList1){
                  System.out.println(str);
             }
              System.out.println("----------------------");
              //将指定元素插入到链表结尾
              LinkedList<String> linkedList = new LinkedList<>();
              linkedList.addLast("a");
              linkedList.addLast("b");
              linkedList.addLast("c");
              for (String str:linkedList){
                  System.out.println(str);
             }
              System.out.println("---------------------------");
              //返回此链表的第一个元素
            
              System.out.println(linkedList.getFirst());
              //返回此链表的最后一个元素
            
              System.out.println(linkedList.getLast());
              System.out.println("-----------------------");
              //移除此链表中的第一个元素,并返回这个元素
              linkedList.removeFirst();
              //移除此链表中的最后一个元素,并返回这个元素
              linkedList.removeLast();
              for (String str:linkedList){
                  System.out.println(str);
             }
              System.out.println("-----------------------");
              linkedList.addLast("c");
              //从此链表所表示的堆栈处弹出一个元素,等效于removeFirst
              linkedList.pop();
              for (String str:linkedList){
                  System.out.println(str);
             }
              System.out.println("-------------------");
              //将元素推入此链表所表示的堆栈 这个等效于addFisrt(E e)
              linkedList.push("h");
              for (String str:linkedList){
                  System.out.println(str);
             }
         }
      }
              

      LinkedList的源码分析

      添加元素

      private static class Node<E> {
          E item;
          Node<E> next;
          Node<E> prev;
          Node(Node<E> prev, E element, Node<E> next) {
              this.item = element;
              this.next = next;
              this.prev = prev;
         }
      }
      

      成员变量

      transient int size = 0;
      
      /**
      * Pointer to first node.
      * Invariant: (first == null && last == null) ||
      *           (first.prev == null && first.item != null)
      */
      
      transient Node<E> first;
      
      /**
      * Pointer to last node.
      * Invariant: (first == null && last == null) ||
      *            (last.next == null && last.item != null)
      */
      
      transient Node<E> last;

      添加元素

      /**
      * Appends the specified element to the end of this list.
      *
      * <p>This method is equivalent to {@link #addLast}.
      *
      * @param e element to be appended to this list
      * @return {@code true} (as specified by {@link Collection#add})
      */
      
      public boolean add(E e) {
          linkLast(e);
          return true;
      }
      
      /**
      * Links e as last element.
      */
      void linkLast(E e) {
          final Node<E> l = last;
          final Node<E> newNode = new Node<>(l, e, null);
          last = newNode;
          if (l == null)
              first = newNode;
          else
              l.next = newNode;
          size++;
          modCount++;
      }

      头尾添加元素

      addFirst

      /**
      * Inserts the specified element at the beginning of this list.
      *
      * @param e the element to add
      */
      
      public void addFirst(E e) {
          linkFirst(e);
      }
      
      /**
      * Links e as first element.
      */
      private void linkFirst(E e) {
          final Node<E> f = first;
          final Node<E> newNode = new Node<>(null, e, f);
          first = newNode;
          if (f == null)
          last = newNode;
          else
              f.prev = newNode;
          size++;
          modCount++;
      }
      

      addLast

      /**
      * Appends the specified element to the end of this list.
      *
      * <p>This method is equivalent to {@link #add}.
      *
      * @param e the element to add
      */
      
      public void addLast(E e) {
          linkLast(e);
      }
      
      /**
      * Links e as last element.
      */
      void linkLast(E e) {
          final Node<E> l = last;
          final Node<E> newNode = new Node<>(l, e, null);
          last = newNode;
          if (l == null)
          first = newNode;
          else
              l.next = newNode;
          size++;
          modCount++;
      }

      获取元素

      /**
      * Returns the element at the specified position in this list.
      *
      * @param index index of the element to return
      * @return the element at the specified position in this list
      * @throws IndexOutOfBoundsException {@inheritDoc}
      */
      
      public E get(int index) {
          checkElementIndex(index);
          return node(index).item;
      }
      private void checkElementIndex(int index) {
          if (!isElementIndex(index))
              throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
      }
      /**
      * Tells if the argument is the index of an existing element.
      */
      
      private boolean isElementIndex(int index) {
          return index >= 0 && index < size;
      }   
      /**
      * Returns the (non-null) Node at the specified element index.
      */
      Node<E> node(int index) {
          // assert isElementIndex(index);
          if (index < (size >> 1)) {
              Node<E> x = first;
              for (int i = 0; i < index; i++)
                  x = x.next;
              return x;
         } else {
              Node<E> x = last;
              for (int i = size - 1; i > index; i--)
                  x = x.prev;
                  return x;
         }
      }
      

      Set接口介绍

      Java基础深化和提高-------容器篇

       Set接口继承自Collection接口,Set接口中没有新增方法,它和 Collection接口保持完全一致。我们在前面学习List接口的使用方 式,在Set中仍然适用。因此,学习Set的使用将没有任何难度。

       Set接口特点

      Set特点:无序、不可重复。无序指Set中的元素没有索引,我们只 能遍历查找;不可重复指不允许加入重复的元素。更确切地讲,新 元素如果和Set中某个元素通过equals()方法对比为true,则只能保 留一个。

       Set常用的实现类有:HashSet、TreeSet等,我们一般使用 HashSet。

       HashSet容器的使用

      HashSet是Set接口的实现类。是Set存储特征的具体实现。

      public class HashSetTest {
          public static void main(String[] args) {
              //实例化HashSet
              Set<String> set = new HashSet<>();
              //添加元素
              set.add("a");
              set.add("b1");
              set.add("c2");
              set.add("d");
              set.add("a");
              //获取元素,在Set容器中没有索引,所以没有对应的get(int index)方法
              for(String str: set){
                  System.out.println(str);
             }
              System.out.println("--------------------");
              //删除元素
              boolean flag = set.remove("c2");
              System.out.println(flag);
              for(String str: set){
                  System.out.println(str);
             }
              System.out.println("------------------------");
              int size = set.size();
              System.out.println(size);
         }
      }

      HashSet存储特征分析

      Java基础深化和提高-------容器篇

       HashSet 是一个不保证元素的顺序且没有重复元素的集合,是线程 不安全的。HashSet允许有null 元素。

       无序:

      在HashSet中底层是使用HashMap存储元素的。HashMap底层使 用的是数组与链表实现元素的存储。元素在数组中存放时,并不是 有序存放的也不是随机存放的,而是对元素的哈希值进行运算决定 元素在数组中的位置。

       不重复:

      当两个元素的哈希值进行运算后得到相同的在数组中的位置时,会 调用元素的equals()方法判断两个元素是否相同。如果元素相同则 不会添加该元素,如果不相同则会使用单向链表保存该元素。

       通过HashSet存储自定义对象

      创建Users对象

      public class Users {
          private String username;
          private int userage;
          public Users(String username, int userage) {
      
          this.username = username;
              this.userage = userage;
         }
          public Users() { }
          @Override
          public boolean equals(Object o) {
              if (this == o) return true;
              if (o == null || getClass() != o.getClass()) return false;
              Users users = (Users) o;
              if (userage != users.userage) return false;
              return username != null ? username.equals(users.username) : users.username == null;
         }
      
          @Override
          public int hashCode() {
              int result = username != null ? username.hashCode() : 0;
              result = 31 * result + userage;
              return result;
             }
          public String getUsername() {
              return username;
            }
          public void setUsername(String username)
            {
              this.username = username;
            }
          public int getUserage() {
              return userage;
            }
          public void setUserage(int userage) {
              this.userage = userage;
            }
          @Override
          public String toString() {
              return "Users{" +
                      "username='" + username + '\'' +
                      ", userage=" + userage +
                     '}';
         }
      }

       在HashSet中存储Users对象

      public class HashSetTest2 {
          public static void main(String[] args) {
              //实例化HashSet
              Set<Users> set = new HashSet<>();
              Users u = new Users("oldlu",18);
              Users u1 = new Users("oldlu",18);
              set.add(u);
              set.add(u1);
              System.out.println(u.hashCode());
              System.out.println(u1.hashCode());
              for(Users users:set){
                  System.out.println(users);
             }
         }
      }
      

      HashSet底层源码分析

      成员变量

      private transient HashMap<E,Object> map;
      // Dummy value to associate with an Object in the backing Map
      private static final Object PRESENT = new Object();

      添加元素

      /**
      * Adds the specified element to this set if it is not already present.
      * More formally, adds the specified element <tt>e</tt> to this set if
      * this set contains no element <tt>e2</tt> such that
      * <tt>(e==null&nbsp;? &nbsp;e2==null&nbsp;:&nbsp;e.equals(e2)) </tt>.
      * If this set already contains the element, the call leaves the set
      * unchanged and returns <tt>false</tt>.
      *
      * @param e element to be added to this set
      * @return <tt>true</tt> if this set did not already contain the specified
      * element
      */
      public boolean add(E e) {
          return map.put(e, PRESENT)==null;
      }

      TreeSet容器的使用

      Java基础深化和提高-------容器篇

       TreeSet实现了Set接口,它是一个可以对元素进行排序的容器。底 层实际是用TreeMap实现的,内部维持了一个简化版的TreeMap, 通过key来存储元素。 TreeSet内部需要对存储的元素进行排序,因 此,我们需要给定排序规则。

      排序规则实现方式:

      1、通过元素自身实现比较规则。

      2、通过比较器指定比较规则。

       

      public class TreeSetTest {
          public static void main(String[] args) {
              //实例化TreeSet
              Set<String> set = new TreeSet<>();
              //添加元素
              set.add("c");
              set.add("a");
              set.add("d");
              set.add("b");
              set.add("a");
              //获取元素
              for(String str :set){
                  System.out.println(str);
             }
         }
      }

      通过元素自身实现比较规则

       Java基础深化和提高-------容器篇

       在元素自身实现比较规则时,需要实现Comparable接口中的 compareTo方法,该方法中用来定义比较规则。TreeSet通过调用 该方法来完成对元素的排序处理。

       创建Users类

      public class Users implements Comparable<Users>{
          private String username;
          private int userage;
          public Users(String username, intuserage) {
              this.username = username;
              this.userage = userage;
         }
         public Users() { }
        
        @Override
          public boolean equals(Object o) {
              System.out.println("equals...");
              if (this == o) return true;
              if (o == null || getClass() != o.getClass()) return false;
              Users users = (Users) o;
              if (userage != users.userage) return false;
              return username != null ? username.equals(users.username) : users.username == null;
         }
          @Override
          public int hashCode() {
              int result = username != null ? username.hashCode() : 0;
              result = 31 * result + userage;
              return result;
         }
          public String getUsername() {
              return username;
         }
      
         public void setUsername(String username)
          {
              this.username = username;
         }
         
         public int getUserage() {
              return userage;
         }
         
         public void setUserage(int userage) {
              this.userage = userage;
         }
         
       @Override
        public String toString() {
              return "Users{" +
                      "username='" + username + '\'' +
                      ", userage=" + userage +
                      '}';
         }
          //定义比较规则
          //正数:大,负数:小,0:相等
          @Override
          public int compareTo(Users o) {
              if(this.userage > o.getUserage()){
                  return 1;
             }
              if(this.userage == o.getUserage()){
                 return this.username.compareTo(o.getUsername());
            }
              return -1;
         }
      }
      Set<Users> set1 = new TreeSet<>();
      Users u = new Users("oldlu",18);
      Users u1 = new Users("admin",22);
      Users u2 = new Users("sxt",22);
      set1.add(u);
      set1.add(u1);
      set1.add(u2);
      for(Users users:set1){
          System.out.println(users);
      }

      通过比较器实现比较规则

      Java基础深化和提高-------容器篇

       通过比较器定义比较规则时,我们需要单独创建一个比较器,比较 器需要实现Comparator接口中的compare方法来定义比较规则。 在实例化TreeSet时将比较器对象交给TreeSet来完成元素的排序处 理。此时元素自身就不需要实现比较规则了。

      创建Student类

      public class Student {
          private String name;
          private int age;
          public Student(String name, int age) {
              this.name = name;
              this.age = age;
         }
          public Student() { }
          
        @Override
          public String toString() {
              return "Student{" +
                      "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;
         }
          @Override
         public boolean equals(Object o) {
              if (this == o) return true;
              if (o == null || getClass() != o.getClass()) return false;
              Student student = (Student) o;
              if (age != student.age) return false;
              return name != null ? name.equals(student.name) : student.name == null;
         }
          @Override
          public int hashCode() {
              int result = name != null ? name.hashCode() : 0;
              result = 31 * result + age;
              return result;
         }
      }

      创建比较器

      public class StudentComparator implements Comparator<Student> {
          //定义比较规则
          @Override
          public int compare(Student o1, Student o2) {
              if(o1.getAge() > o2.getAge()){
                  return 1;
             }
             
              if(o1.getAge() == o2.getAge()){
                  return o1.getName().compareTo(o2.getName());
             }
            return -1;
         }
      }
      public class TreeSetTest3 {
          public static void main(String[] args) {
              //创建TreeSet容器,并给定比较器对象
              Set<Student> set = new TreeSet<>(new StudentComparator());
              Student s = new Student("oldlu",18);
              Student s1 = new Student("admin",22);
              Student s2 = new Student("sxt",22);
              set.add(s);
              set.add(s1);
              set.add(s2);
              for(Student student:set){
                  System.out.println(student);
             }
         }
      }

      TreeSet底层源码分析

      成员变量

      /**
      * The backing map.
      */
      private transient NavigableMap<E,Object> m;
      
      // Dummy value to associate with an Object in the backing Map
      private static final Object PRESENT = new Object();
      

      构造方法

      public TreeSet() {
          this(new TreeMap<E,Object>());
      }

      添加元素

      /**
      * Adds the specified element to this set if it is not already present.
      * More formally, adds the specified element <tt>e</tt> to this set if
      * this set contains no element <tt>e2</tt> such that
      * <tt>(e==null&nbsp;? &nbsp;e2==null&nbsp;:&nbsp;e.equals(e2))</tt>.
      * If this set already contains the element, the call leaves the set
      * unchanged and returns <tt>false</tt>.
      *
      * @param e element to be added to this set
      * @return <tt>true</tt> if this set did not already contain the specified
      * element
      */
      
      public boolean add(E e) {
          return map.put(e, PRESENT)==null;
      }

      单例集合使用案例

      需求: 产生1-10之间的随机数([1,10]闭区间),将不重复的10个随机数放到 容器中。

       使用List类型容器实现

      public class ListDemo {
          public static void main(String[] args) {
              List<Integer> list = new ArrayList<>();
             while(true){
                 //产生随机数
                 int num = (int) (Math.random()*10+1);
                  //判断当前元素在容器中是否存在
                 if(!list.contains(num)){
                      list.add(num);
                 }
                 //结束循环
                 if(list.size() == 10){
                     break;
                 }
             }
             for(Integer i:list){
                 System.out.println(i);
             }
         }
      }

      使用Set类型容器实现

      public class SetDemo {
          public static void main(String[] args) {
              Set<Integer> set = new HashSet<>();
              while(true){
                  int num = (int)(Math.random()*10+1);
                  //将元素添加容器中,由于Set类型容器是、不允许有重复元素的,所以不需要判断。
                  set.add(num);
                  //结束循环
                  if(set.size() == 10){
                      break;
                 }
             }
              for(Integer i:set){
                  System.out.println(i);
             }
         }
      }
      

      双例集合

      Java基础深化和提高-------容器篇

       Map接口介绍

      Map接口定义了双例集合的存储特征,它并不是Collection接口的 子接口。双例集合的存储特征是以key与value结构为单位进行存 储。体现的是数学中的函数 y=f(x)感念。

      Map与Collecton的区别:

       1、Collection中的容器,元素是孤立存在的(理解为单身),向集 合中存储元素采用一个个元素的方式存储。

      2、Map中的容器,元素是成对存在的(理解为现代社会的夫妻)。每 个元素由键与值两部分组成,通过键可以找对所对应的值。

      3、Collection中的容器称为单列集合,Map中的容器称为双列集 合。

      4、Map中的集合不能包含重复的键,值可以重复;每个键只能对应 一个值。

      5、Map中常用的容器为HashMap,TreeMap等。

      Map接口中常用的方法表

      Java基础深化和提高-------容器篇

       HashMap容器的使用

      HashMap采用哈希算法实现,是Map接口最常用的实现类。 由于 底层采用了哈希表存储数据,我们要求键不能重复,如果发生重 复,新的键值对会替换旧的键值对。 HashMap在查找、删除、修 改方面都有非常高的效率。

      public class HashMapTest {
          public static void main(String[] args) {
              //实例化HashMap容器
              Map<String,String> map = new HashMap<>();
              //添加元素
              map.put("a","A");
              map.put("b","B");
              map.put("c","C");
              map.put("a","D");
              //获取容器中元素数量
              int size = map.size();
              System.out.println(size);
              System.out.println("---------------");
          
              //获取元素
              //方式一
              String v = map.get("a");
              System.out.println(v);
              System.out.println("---------------");
              
              //方式二
              Set<String> keys = map.keySet();
              for(String key:keys){
                  String v1 = map.get(key);
                  System.out.println(key+" ----"+v1);
             }
              System.out.println("-------------------");
              
              //方式三
              Set<Map.Entry<String,String>> entrySet = map.entrySet();
              for(Map.Entry<String,String> entry:entrySet){
                  String key = entry.getKey();
                  String v2 = entry.getValue();
                  System.out.println(key+" ---------- "+v2);
             }
              System.out.println("--------------------");
              //Map容器的并集操作
              Map<String,String> map2 = new HashMap<>();
              map2.put("f","F");
              map2.put("c","CC");
              map.putAll(map2);
              Set<String> keys2 = map.keySet();
              for(String key:keys2){
                  System.out.println("key: "+key+" Value: "+map.get(key));
             }
              System.out.println("---------------");
              //删除元素
              String v3 = map.remove("a");
              System.out.println(v3);
              Set<String> keys3 = map.keySet();
              for(String key:keys3){
                  System.out.println("key: "+key+" Value: "+map.get(key));
             }
              System.out.println("-------------------");
              //判断Key是否存在
              boolean b = map.containsKey("b");
              System.out.println(b);
              //判断Value是否存在
              boolean cc = map.containsValue("CC");
              System.out.println(cc);
      
          }
      }

      HashTable类和HashMap用法几乎一样,底层实现几乎一样,只不 过HashTable的方法添加了synchronized关键字确保线程同步检 查,效率较低。

      HashMap与HashTable的区别

      1 HashMap: 线程不安全,效率高。允许key或value为null

      2 HashTable: 线程安全,效率低。不允许key或value为null

       HashMap的底层源码分析

      底层存储介绍

      HashMap底层实现采用了哈希表,这是一种非常重要的数据结构。 对于我们以后理解很多技术都非常有帮助。 数据结构中由数组和链表来实现对数据的存储,他们各有特点。

      (1) 数组:占用空间连续。 寻址容易,查询速度快。但是,增加和 删除效率非常低。

      (2) 链表:占用空间不连续。 寻址困难,查询速度慢。但是,增加 和删除效率非常高。 那么,我们能不能结合数组和链表的优点(即查询快,增删效率也 高)呢? 答案就是“哈希表”。 哈希表的本质就是“数组+链表”。

       Java基础深化和提高-------容器篇

       Oldlu建议

      对于本章中频繁出现的“底层实现”讲解,建议学有余力的童鞋将 它搞通。刚入门的童鞋如果觉得有难度,可以暂时跳过。入门 期间,掌握如何使用即可,底层原理是扎实内功,便于大家应 对一些大型企业的笔试面试。

       HashMap中的成员变量

      /**
      * The default initial capacity - MUST be a power of two.
      */
      
      static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
      
      /**
      * The maximum capacity, used if a higher value is implicitly specified
      * by either of the constructors with arguments.
      * MUST be a power of two <= 1<<30.
      */
      
      static final int MAXIMUM_CAPACITY = 1 << 30;
      
      /**
      * The load factor used when none specified in constructor.
      */
      
      static final float DEFAULT_LOAD_FACTOR = 0.75f;
      /**
      * The bin count threshold for using a tree rather than list for a
      * bin. Bins are converted to trees when adding an element to a
      * bin with at least this many nodes. The value must be greater
      * than 2 and should be at least 8 to mesh with assumptions in
      * tree removal about conversion back to plain bins upon
      * shrinkage.
      */
      
      static final int TREEIFY_THRESHOLD = 8;
      /**
      * The bin count threshold for untreeifying a (split) bin during a
      * resize operation. Should be less than TREEIFY_THRESHOLD, and at
      * most 6 to mesh with shrinkage detection under removal.
      */
      
      static final int UNTREEIFY_THRESHOLD = 6;
      /**
      * The smallest table capacity for which bins may be treeified.
      * (Otherwise the table is resized if too many nodes in a bin.)
      * Should be at least 4 * TREEIFY_THRESHOLD to avoid conflicts
      * between resizing and treeification thresholds.
      */
      static final int MIN_TREEIFY_CAPACITY = 64;
      /**
      * The number of key-value mappings contained in this map.
      */
      
      transient int size;
      /**
      * The table, initialized on first use, and resized as
      * necessary. When allocated, length is always a power of two.
      * (We also tolerate length zero in some operations to allow
      * bootstrapping mechanics that are currently not needed.)
      */
      
      transient Node<K,V>[] table;

      HashMap中存储元素的节点类型

       Node类

      static class Node<K,V> implements
      Map.Entry<K,V> {
          final int hash;
          final K key;
          V value;
          Node<K,V> next;
          Node(int hash, K key, V value, Node<K,V> next) {
              this.hash = hash;
              this.key = key;
              this.value = value;
              this.next = next;
         }
          
      public final K getKey()       { return key; }
          
      public final V getValue()     { return value; }
          
      public final String toString() { return key + "=" + value; }
          
      public final int hashCode() { return Objects.hashCode(key) ^ Objects.hashCode(value); }
          
      public final V setValue(V newValue) {
              V oldValue = value;
              value = newValue;
              return oldValue;
         }
          
      public final boolean equals(Object o) {
              if (o == this)
                  return true;
              if (o instanceof Map.Entry) {
                  Map.Entry<?,?> e = (Map.Entry<?,?>)o;
                  if (Objects.equals(key, e.getKey()) && Objects.equals(value, e.getValue()))
                      return true;
             }
              return false;
         }
      }

      TreeNode类

      /**
      * Entry for Tree bins. Extends LinkedHashMap.Entry (which in turn
      * extends Node) so can be used as extension of either regular or
      * linked node.
      */
      static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
          TreeNode<K,V> parent;  // red-black tree links
          TreeNode<K,V> left;
          TreeNode<K,V> right;
          TreeNode<K,V> prev;    // needed to unlink next upon deletion
          boolean red;
          TreeNode(int hash, K key, V val, Node<K,V> next) {
              super(hash, key, val, next);
         }
          /**
           * Returns root of tree containing thisnode.
           */
          final TreeNode<K,V> root() {
              for (TreeNode<K,V> r = this, p;;) {
                  if ((p = r.parent) == null)
                      return r;
                  r = p;
             }
         }

      它们的继承关系

      Java基础深化和提高-------容器篇

       HashMap中的数组初始化

      在JDK1.8的HashMap中对于数组的初始化采用的是延迟初始化方 式。通过resize方法实现初始化处理。resize方法既实现数组初始 化,也实现数组扩容处理。

       

      /**
      * Initializes or doubles table size. If null, allocates in
      * accord with initial capacity target held in field threshold.
      * Otherwise, because we are using power-oftwo expansion, the
      * elements from each bin must either stay at same index, or move
      * with a power of two offset in the new table.
      *
      * @return the table
      */
      final Node<K,V>[] resize() {
          Node<K,V>[] 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 initialthreshold 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<K,V>[] newTab = (Node<K,V> [])new Node[newCap];
              table = newTab;
          if (oldTab != null) {
              for (int j = 0; j < oldCap; ++j) {
                  Node<K,V> 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<K,V>)e).split(this, newTab, j, oldCap);
                      else { // preserve order
                          Node<K,V> loHead = null,loTail = null;
                          Node<K,V> hiHead = null,hiTail = null;
                          Node<K,V> 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中计算Hash值

      1 获得key对象的hashcode

      首先调用key对象的hashcode()方法,获得key的hashcode值。

      2 根据hashcode计算出hash值(要求在[0, 数组长度-1]区 间)hashcode是一个整数,我们需要将它转化成[0, 数组长度-1] 的范围。我们要求转化后的hash值尽量均匀地分布在[0,数组长 度-1]这个区间,减少“hash冲突”

           2.1  一种极端简单和低下的算法是: hash值 = hashcode/hashcode; 也就是说,hash值总是1。意味着,键值对对象都会存储到 数组索引1位置,这样就形成一个非常长的链表。相当于每存 储一个对象都会发生“hash冲突”,HashMap也退化成了一个 “链表”。

           2.2  一种简单和常用的算法是(相除取余算法): hash值 = hashcode%数组长度;

           这种算法可以让hash值均匀的分布在[0,数组长度-1]的区间。 但是,这种算法由于使用了“除法”,效率低下。JDK后来改进 了算法。首先约定数组长度必须为2的整数幂,这样采用位运 算即可实现取余的效果:hash值 = hashcode&(数组长 度-1)。

      /**
      * Associates the specified value with the specified key in this map.
      * If the map previously contained a mapping for the key, the old
      * value is replaced.
      *
      * @param key key with which the specified value is to be associated
      * @param value value to be associated with the specified key
      * @return the previous value associated with <tt>key</tt>, or
      * <tt>null</tt> if there was no mapping for <tt>key</tt>.
      *         (A <tt>null</tt> return can also indicate that the map
      *         previously associated <tt>null</tt> with <tt>key</tt>.)
      */
      
      public V put(K key, V value) {
          return putVal(hash(key), key, value, false, true);
      }
      
      static final int hash(Object key) {
          int h;
          return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
      }
      

       

      /**
      * Implements Map.put and related methods
      *
      * @param hash hash for key
      * @param key the key
      * @param value the value to put
      * @param onlyIfAbsent if true, don't change existing value
      * @param evict if false, the table is in creation mode.
      * @return previous value, or null if none
      */
      final V putVal(int hash, K key, V value,
      boolean onlyIfAbsent,
                     boolean evict) {
          Node<K,V>[] tab; Node<K,V> 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<K,V> 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<K,V>)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;
      }

      HashMap中添加元素

      /**
      * Associates the specified value with the specified key in this map.
      * If the map previously contained a mapping for the key, the old
      * value is replaced.
      *
      * @param key key with which the specified value is to be associated
      * @param value value to be associated with the specified key
      * @return the previous value associated with <tt>key</tt>, or
      *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
      *         (A <tt>null</tt> return can also indicate that the map
      *         previously associated <tt>null</tt> with <tt>key</tt>.)
      */
      
      public V put(K key, V value) {
          return putVal(hash(key), key, value,false, true);
      }

      HashMap中数组扩容

      /**
      * Implements Map.put and related methods
      *
      * @param hash hash for key
      * @param key the key
      * @param value the value to put
      * @param onlyIfAbsent if true, don't change
      existing value
      * @param evict if false, the table is in creation mode.
      * @return previous value, or null if none
      */
      final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
          Node<K,V>[] tab; Node<K,V> 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<K,V> 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<K,V>)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;
      }
      /**
      * Initializes or doubles table size. If null, allocates in
      * accord with initial capacity target held in field threshold.
      * Otherwise, because we are using power-oftwo expansion, the
      * elements from each bin must either stay at same index, or move
      * with a power of two offset in the new table.
      *
      * @return the table
      */
      final Node<K,V>[] resize() {
           Node<K,V>[] 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<K,V>[] newTab = (Node<K,V> [])new Node[newCap];
              table = newTab;
          if (oldTab != null) {
              for (int j = 0; j < oldCap; ++j) {
                  Node<K,V> 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<K,V>)e).split(this, newTab, j, oldCap);
                      else { // preserve order
                          Node<K,V> loHead = null, loTail = null;
                          Node<K,V> hiHead = null, hiTail = null;
                          Node<K,V> 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;
      }

      TreeMap容器的使用

      Java基础深化和提高-------容器篇

       TreeMap和HashMap同样实现了Map接口,所以,对于API的用法 来说是没有区别的。HashMap效率高于TreeMap;TreeMap是可 以对键进行排序的一种容器,在需要对键排序时可选用TreeMap。 TreeMap底层是基于红黑树实现的。

      在使用TreeMap时需要给定排序规则:

      1、元素自身实现比较规则

      2、通过比较器实现比较规则

       元素自身实现比较规则

      public class Users implements
      Comparable<Users>{
          private String username;
          private int userage;
      public Users(String username, int userage) {
              this.username = username;
              this.userage = userage;
         }
          public Users() { }
          @Override
          public boolean equals(Object o) {
              System.out.println("equals...");
              if (this == o) return true;
              if (o == null || getClass() != o.getClass()) return false;
              Users users = (Users) o;
              if (userage != users.userage) return false;
              return username != null ? username.equals(users.username) : users.username == null;
         }
          @Override
          public int hashCode() {
              int result = username != null ? username.hashCode() : 0;
              result = 31 * result + userage;
              return result;
      }
          public String getUsername() {
              return username;
         }
          public void setUsername(String username)
           {
              this.username = username;
           }
          public int getUserage() {
              return userage;
           }
          public void setUserage(int userage) {
              this.userage = userage;
         }
          @Override
          public String toString() {
              return "Users{" +
                      "username='" + username + '\'' +
                      ", userage=" + userage +
                      '}';
         }
          //定义比较规则
          //正数:大,负数:小,0:相等
          @Override
          public int compareTo(Users o) {
             if(this.userage < o.getUserage()){
                  return 1;
             }
              if(this.userage == o.getUserage()){
                 return this.username.compareTo(o.getUsername());
             }
              return -1;
         }
      }
      
      public class TreeMapTest {
          public static void main(String[] args) {
              //实例化TreeMap
              Map<Users,String> map = new TreeMap<>();
              Users u1 = new Users("oldlu",18);
              Users u2 = new Users("admin",22);
              Users u3 = new Users("sxt",22);
              map.put(u1,"oldlu");
              map.put(u2,"admin");
              map.put(u3,"sxt");
              Set<Users> keys = map.keySet();
              for(Users key :keys){
                  System.out.println(key+" --------- "+map.get(key));
             }
         }
      }

      通过比较器实现比较规则

      public class Student {
          private String name;
          private int age;
          public Student(String name, int age) {
              this.name = name;
              this.age = age;
         }
          public Student() { }
          @Override
          public String toString() {
              return "Student{" +
                      "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;
         }
          @Override
          public boolean equals(Object o) {
              if (this == o) return true;
              if (o == null || getClass() != o.getClass()) return false;
              Student student = (Student) o;
              if (age != student.age) return false;
              return name != null ? name.equals(student.name) : student.name == null;
         }
          @Override
          public int hashCode() {
              int result = name != null ? name.hashCode() : 0;
              result = 31 * result + age;
              return result;
         }
      }
      
      public class StudentComparator implements Comparator<Student> {
          //定义比较规则
        @Override
          public int compare(Student o1, Student o2) {
              if(o1.getAge() > o2.getAge()){
                  return 1;
             }
              if(o1.getAge() == o2.getAge()){
                  return
                 o1.getName().compareTo(o2.getName());
             }
              return -1;
         }
      }
      
      public class TreeMapTest {
          public static void main(String[] args) {
                Map<Student,String> treeMap = new TreeMap<>(new StudentComparator());
          Student s1 = new Student("oldlu",18);
          Student s2 = new Student("admin",22);
          Student s3 = new Student("sxt",22);
          treeMap.put(s1,"oldlu");
          treeMap.put(s2,"admin");
          treeMap.put(s3,"sxt");
          Set<Student> keys1 = treeMap.keySet();
          for(Student key :keys1){
              System.out.println(key+" ----"+treeMap.get(key));
           }
         }
      }

      TreeMap的底层源码分析

      TreeMap是红黑二叉树的典型实现。我们打开TreeMap的源码,发 现里面有一行核心代码:

      private transient Entry<K,V> root = null;

       root用来存储整个树的根节点。我们继续跟踪Entry(是TreeMap的 内部类)的代码:

       Java基础深化和提高-------容器篇

       可以看到里面存储了本身数据、左节点、右节点、父节点、以及节 点颜色。 TreeMap的put()/remove()方法大量使用了红黑树的理 论。在本节课中,不再展开。需要了解更深入的,可以参考专门的 数据结构书籍。 TreeMap和HashMap实现了同样的接口Map,因此,用法对于调 用者来说没有区别。HashMap效率高于TreeMap;在需要排序的 Map时才选用TreeMap。

       Iterator接口

      Java基础深化和提高-------容器篇

       Iterator迭代器接口介绍

      Collection接口继承了Iterable接口,在该接口中包含一个名为 iterator的抽象方法,所有实现了Collection接口的容器类对该方法 做了具体实现。iterator方法会返回一个Iterator接口类型的迭代器 对象,在该对象中包含了三个方法用于实现对单例容器的迭代处 理。

       Iterator对象的工作原理:

      Java基础深化和提高-------容器篇

       Iterator接口定义了如下方法:

      1 boolean hasNext(); //判断游标当前位置的下一个位置是否还有元素没有被遍历;

      2 Object next(); //返回游标当前位置的下一个元素并将游标移动到下一个位置;

      3 void remove(); //删除游标当前位置的元素,在执行完next后该操作只能执行一次;

       Iterator迭代器的使用

      迭代List接口类型容器

      public class IteratorListTest {
          public static void main(String[] args) {
              //实例化容器
              List<String> list  = new ArrayList<>();
              list.add("a");
              list.add("b");
              list.add("c");
              //获取元素
              //获取迭代器对象
              Iterator<String> iterator = list.iterator();
              //方式一:在迭代器中,通过while循环获取元素
              while(iterator.hasNext()){
                  String value = iterator.next();
                  System.out.println(value);
             }
              System.out.println("-------------------------------");
              //方法二:在迭代器中,通过for循环获取元素
              for(Iterator<String> it = list.iterator();it.hasNext();){
                  String value = it.next();
                  System.out.println(value);
             }
         }
      }

      迭代Set接口类型容器

      public class IteratorSetTest {
          public static void main(String[] args) {
              //实例化Set类型的容器
              Set<String> set  = new HashSet<>();
              set.add("a");
              set.add("b");
              set.add("c");
              //方式一:通过while循环
              //获取迭代器对象
              Iterator<String> iterator = set.iterator();
              while(iterator.hasNext()){
                  String value = iterator.next();
                  System.out.println(value);
                 }
              System.out.println("-------------------------");
              //方式二:通过for循环
              for(Iterator<String> it = set.iterator();it.hasNext();){
                  String value = it.next();
                  System.out.println(value);
             }
         }
      }

      迭代Map接口类型容器

      public class IteratorMapTest {
          public static void main(String[] args) {
              //实例化HashMap容器
              Map<String, String> map = new HashMap<String, String>();
              //添加元素
              map.put("a", "A");
              map.put("b", "B");
              map.put("c", "C");
              //遍历Map容器方式一
              Set<String> keySet = map.keySet();
              for (Iterator<String> it = keySet.iterator(); it.hasNext();){
                  String key = it.next();
                  String value = map.get(key);
                  System.out.println(key+" ------------- "+value);
             }
              System.out.println("------------------------");
              //遍历Map容器方式二
              Set<Map.Entry<String, String>> entrySet = map.entrySet();
              Iterator<Map.Entry<String, String>> iterator = entrySet.iterator();
              while(iterator.hasNext()){
                  Map.Entry entry = iterator.next();
                
               System.out.println(entry.getKey()+" ------------ "+ entry.getValue());
             }
         }
      }

      在迭代器中删除元素

      public class IteratorRemoveTest {
          public static void main(String[] args) {
              List<String> list = new ArrayList<>();
              list.add("a");
              list.add("b");
              list.add("c");
              list.add("d");
              Iterator<String> iterator = list.iterator();
              while(iterator.hasNext()){
                  //不要在一次循环中多次调用next方法。
                  String value = iterator.next();
                  iterator.remove();
             }
              System.out.println("----------------");
              for(Iterator<String> it = list.iterator();
                 it.hasNext();){
                  System.out.println(it.next());
                  list.add("dddd");
             }
         }
      }

      遍历集合的方法总结

      遍历List方法一:普通for循环

      for(int i=0;i<list.size();i++){//list为集合的对象名
       String temp = (String)list.get(i);
       System.out.println(temp);
      }

      遍历List方法二:增强for循环(使用泛型!)

      for (String temp : list) {
       System.out.println(temp);
      }

      遍历List方法三:使用Iterator迭代器(1)

      for(Iterator iter=
      list.iterator();iter.hasNext();){
       String temp = (String)iter.next();
       System.out.println(temp);
      }

      遍历List方法四:使用Iterator迭代器(2)

      Iterator  iter =list.iterator();
      while(iter.hasNext()){
       Object  obj =  iter.next();
       iter.remove();//如果要遍历时,删除集合中的元素,建议使用这种方式!
       System.out.println(obj);
      }
      

      遍历Set方法一:增强for循环

      for(String temp:set){
       System.out.println(temp);
      }

      遍历Set方法二:使用Iterator迭代器

      for(Iterator iter =
      set.iterator();iter.hasNext();){
       String temp = (String)iter.next();
       System.out.println(temp);
      }

      遍历Map方法一:根据key获取value

      Map<Integer, Man> maps = new HashMap<Integer,
      Man>();
      Set<Integer>  keySet =  maps.keySet();
      for(Integer id : keySet){
       System.out.println(maps.get(id).name);
      }

      遍历Map方法二:使用entrySet

      Set<Map.Entry<Integer, Man>>  ss =
      maps.entrySet();
      for (Iterator<Map.Entry<Integer, Man>>
      iterator = ss.iterator();
      iterator.hasNext();) {
       Map.Entry e =  iterator.next();
       System.out.println(e.getKey()+"--
      "+e.getValue());
      }

      Collections工具类

      Java基础深化和提高-------容器篇

       类 java.util.Collections 提供了对Set、List、Map进行排序、填充、 查找元素的辅助方法。

      Java基础深化和提高-------容器篇

       Collections工具类的常用方法

      public class CollectionsTest {
          public static void main(String[] args) {
              List<String> list = new ArrayList<>();
              list.add("c");
              list.add("b");
              list.add("a");
              //对元素排序
              Collections.sort(list);
              for(String str:list){
                  System.out.println(str);
             }
              System.out.println("-------------------");
              List<Users> list2 = new ArrayList<>();
              Users u = new Users("oldlu",18);
              Users u2 = new Users("sxt",22);
              Users u3 = new Users("admin",22);
              list2.add(u);
              list2.add(u2);
              list2.add(u3);
              //对元素排序
              Collections.sort(list2);
              for(Users user:list2){
                  System.out.println(user);
             }
              System.out.println("-------------------");
              List<Student> list3 = new ArrayList<>();
              Student s = new Student("oldlu",18);
              Student s1 = new Student("sxt",20);
              Student s2 = new Student("admin",20);
              
              list3.add(s);
              list3.add(s1);
              list3.add(s2);
              
              Collections.sort(list3,new StudentComparator());
              for(Student student:list3){
                  System.out.println(student);
             }
              System.out.println("-------------------");
              List<String> list4 = new ArrayList<>();
              list4.add("a");
              list4.add("b");
              list4.add("c");
              list4.add("d");
              //洗牌
              Collections.shuffle(list4);
              for(String str:list4){
                  System.out.println(str);
             }
         }
      }
      

       

      版权声明:本文内容来自第三方投稿或授权转载,原文地址:https://tongxiaochun.blog.csdn.net/article/details/125235962,作者:童小纯,版权归原作者所有。本网站转在其作品的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如因作品内容、版权等问题需要同本网站联系,请发邮件至ctyunbbs@chinatelecom.cn沟通。

      上一篇:二十三种设计模式-----归纳篇(JAVA)

      下一篇:Spring Boot Jar 包启动时如何加载外部资源

      相关文章

      2025-05-19 09:04:53

      容器技术-Docker 容器的端口发布

      容器技术-Docker 容器的端口发布

      2025-05-19 09:04:53
      Docker , 容器 , 指定 , 映射 , 端口
      2025-05-14 10:02:58

      java休眠到指定时间怎么写

      java休眠到指定时间怎么写

      2025-05-14 10:02:58
      java , sleep , Thread , util , 方法
      2025-05-14 10:02:58

      java项目多端数据同步解决方案

      多端数据同步是指在多个设备(例如桌面应用、移动应用、Web应用)之间保持数据的一致性。

      2025-05-14 10:02:58
      java , Spring , WebSocket , 同步 , 数据 , 版本号
      2025-05-14 09:51:21

      Docker大学生看了都会系列(十、Docker网络)

      docker使用Linux桥接网卡,在宿主机虚拟一个docker容器网桥(docker0),docker启动一个容器时会根据docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网络网关。

      2025-05-14 09:51:21
      docker , Docker , 容器 , 宿主机 , 模式 , 网桥 , 网络
      2025-05-14 09:51:21

      Docker大学生看了都会系列(三、常用帮助、镜像、容器命令)

      Docker大学生看了都会系列(三、常用帮助、镜像、容器命令)

      2025-05-14 09:51:21
      container , docker , 命令 , 容器 , 查看 , 镜像
      2025-05-13 09:49:12

      Java学习(动态代理的思想详细分析与案例准备)(1)

      Java学习(动态代理的思想详细分析与案例准备)(1)

      2025-05-13 09:49:12
      java , 代理 , 代码 , 对象 , 接口 , 方法 , 需要
      2025-05-12 08:43:47

      盛最多水的容器

      给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。

      2025-05-12 08:43:47
      lt , 容器 , 示例
      2025-05-09 08:50:35

      STL:Stack和Queue的模拟实现

      适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),该种模式是将一个类的接口转换成客户希望的另外一个接口。

      2025-05-09 08:50:35
      deque , queue , stack , 元素 , 容器 , 底层 , 适配器
      2025-05-09 08:20:32

      基于IDEA的Maven简单工程创建及结构分析

      通过一个 mvn 命令直接让我们创建一个 Maven 的脚手架。

      2025-05-09 08:20:32
      java , Maven , xml , 创建 , 文件 , 文件夹 , 项目
      2025-05-09 08:20:32

      STL:模版进阶 | Priority_queue的模拟实现

      模板参数分类为类型形参与非类型形参。

      2025-05-09 08:20:32
      函数 , 参数 , 容器 , 模板 , 模版 , 类型
      查看更多
      推荐标签

      作者介绍

      天翼云小翼
      天翼云用户

      文章

      33561

      阅读量

      5266564

      查看更多

      最新文章

      java项目多端数据同步解决方案

      2025-05-14 10:02:58

      Java学习(动态代理的思想详细分析与案例准备)(1)

      2025-05-13 09:49:12

      STL:Stack和Queue的模拟实现

      2025-05-09 08:50:35

      前K个高频元素java

      2025-05-08 09:03:57

      基于java Swing开发的学生成绩管理系统【项目源码+数据库脚本】

      2025-05-08 09:03:21

      java Swing学生成绩管理系统【项目源码+数据库脚本】

      2025-05-08 09:03:21

      查看更多

      热门文章

      JAVA__接口的作用

      2023-04-18 14:14:13

      Java学习之算术运算符两只老虎

      2023-04-19 09:23:13

      排序算法Java版-归并排序算法

      2023-04-24 11:25:19

      JAVA多线程学习笔记

      2023-05-11 06:05:48

      try...catch...finally java

      2023-03-29 09:40:26

      Java:apache.poi读写Excel文件

      2023-02-22 06:40:54

      查看更多

      热门标签

      java Java python 编程开发 代码 开发语言 算法 线程 Python html 数组 C++ 元素 javascript c++
      查看更多

      相关产品

      弹性云主机

      随时自助获取、弹性伸缩的云服务器资源

      天翼云电脑(公众版)

      便捷、安全、高效的云电脑服务

      对象存储

      高品质、低成本的云上存储服务

      云硬盘

      为云上计算资源提供持久性块存储

      查看更多

      随机文章

      一行脚本快速统计项目java代码行数

      使用可变对象作为Java Map的key,会带来潜在风险的一个例子

      Java错误消息sun.security.validator.ValidatorException应该如何处理

      经典面试问题——OOP语言的三大特征

      一个简单的Java测试类型服务器

      spring源码分析之循环依赖

      • 7*24小时售后
      • 无忧退款
      • 免费备案
      • 专家服务
      售前咨询热线
      400-810-9889转1
      关注天翼云
      • 旗舰店
      • 天翼云APP
      • 天翼云微信公众号
      服务与支持
      • 备案中心
      • 售前咨询
      • 智能客服
      • 自助服务
      • 工单管理
      • 客户公告
      • 涉诈举报
      账户管理
      • 管理中心
      • 订单管理
      • 余额管理
      • 发票管理
      • 充值汇款
      • 续费管理
      快速入口
      • 天翼云旗舰店
      • 文档中心
      • 最新活动
      • 免费试用
      • 信任中心
      • 天翼云学堂
      云网生态
      • 甄选商城
      • 渠道合作
      • 云市场合作
      了解天翼云
      • 关于天翼云
      • 天翼云APP
      • 服务案例
      • 新闻资讯
      • 联系我们
      热门产品
      • 云电脑
      • 弹性云主机
      • 云电脑政企版
      • 天翼云手机
      • 云数据库
      • 对象存储
      • 云硬盘
      • Web应用防火墙
      • 服务器安全卫士
      • CDN加速
      热门推荐
      • 云服务备份
      • 边缘安全加速平台
      • 全站加速
      • 安全加速
      • 云服务器
      • 云主机
      • 智能边缘云
      • 应用编排服务
      • 微服务引擎
      • 共享流量包
      更多推荐
      • web应用防火墙
      • 密钥管理
      • 等保咨询
      • 安全专区
      • 应用运维管理
      • 云日志服务
      • 文档数据库服务
      • 云搜索服务
      • 数据湖探索
      • 数据仓库服务
      友情链接
      • 中国电信集团
      • 189邮箱
      • 天翼企业云盘
      • 天翼云盘
      ©2025 天翼云科技有限公司版权所有 增值电信业务经营许可证A2.B1.B2-20090001
      公司地址:北京市东城区青龙胡同甲1号、3号2幢2层205-32室
      • 用户协议
      • 隐私政策
      • 个人信息保护
      • 法律声明
      备案 京公网安备11010802043424号 京ICP备 2021034386号