Java 中的泛型 集合(List,Set) Map

news/2024/11/18 21:34:08/文章来源:https://www.cnblogs.com/ihave2carryon/p/18292200

泛型 集合(List,Set) Map

泛型

泛型的本质是参数化类型,即允许在编译时对集合进行类型检查,从而避免安全问题,提高代码的复用性

泛型的具体定义与作用

  • 定义:泛型是一种在编译阶段进行类型检查的机制,它允许在类,方法,接口后通过<> 来声明类型参数.这些参数在编译时会被具体的类型替换.java在运行时,会通过类型擦除机制,将泛型类型擦除,变为原始类型(如,String,Integer),具体的例子将在”泛型的使用”中演示
  • 作用:
  1. 类型安全:通过泛型,在编译阶段可以检查到更多的类型错误,就不用再运行时抛出ClassCastException
  2. 消除强制转化:在使用了泛型之后,很多的类型转换都可以自动执行,减少了代码中的显性强制转换
  3. 提高了代码的复用性

泛型的使用

  • 泛型类的使用:在类名后添加参数声明部分(用<> 包括起来),如 class Box<T>
public class Box<T>
{private  T value;//定义泛型值public T getValue() {return value;}public void setValue(T value) {this.value = value;}public static void main(String[] args) {Box<String> stringBox = new Box<>();//实例化String类的泛型对象stringBox.setValue("这是一个String类型范式");System.out.println(stringBox.getValue());Box<Integer> integerBox = new Box<>();//实例化Integer类的泛型对象integerBox.setValue(123);System.out.println(integerBox.getValue());}
}
  • 泛型类的使用:与泛型类类似,在接口定义后添加类似参数声明.如interface<K,V>
public interface Pair <K,V>{  //泛型接口K getKey();  //Pair 接口定义了两个抽象方法 类型分别为 K,VV getValue(); //K,V都是待定义的类型
}
public class SimplePair<K,V> implements Pair<K,V>{private K key;private V value;//SimplePair类实现了 Pair接口 必须从写其中的抽象方法public SimplePair(K key, V value) {this.key = key;    //SimplePair的构造函数this.value = value;}@Overridepublic K getKey() {return key;}@Overridepublic V getValue() {return value;}public static void main(String[] args) {//实例化 一个SimplePair对象 传入参数为 Integer,StringSimplePair<Integer, String> integerStringSimplePair = new SimplePair<Integer, String>(1,"one");//则K就代表Integer V就代表StringSystem.out.println(integerStringSimplePair.getKey());System.out.println(integerStringSimplePair.getValue());}
}
  • 泛型方法:在方法返回类型前声明类型参数,如public <T> void printArray(T[] inputArray)。泛型方法可以定义在泛型类中,也可以定义在非泛型类中。
public class Method {public static <T> void printArray(T[] inputArray){ //定义了一个返回值为<T>的泛型函数for (T element : inputArray) {System.out.println(element);}}public static void main(String[] args) {Integer [] integers={1,2,3,4,5};String [] strings={"abcdefg"};printArray(integers);//调用泛型函数printArray(strings);}
}
  • 类型通配标识符:使用? 表示类型实参,表示不确定的类型(或者是待定的类型),通常用于泛型方法,泛型类,泛型接口;通常应用于当你不确定该数据类型时
 public static void printElements(List<?> list)//假设你要写一个打印集合元素的方法//当你不确定该集合的类型 则可以使用通配符{for (Object o : list) {System.out.println(o);}}public static void main(String[] args) {List<String> stringList=new ArrayList<>();List<Integer> integerList=new ArrayList<>();printElements(stringList);//可以打印StringprintElements(integerList);//也可以打印Integer}
  • 上限通配符:上限通配符用于知道一个类型的上限,它允许你指定一个类型及其子类型,其使用格式为<?extends Type >
List<? extends Number> listOfNum=new ArrayList<Integer>();//使用Integer是合法的//因为Number的子类包括 Integer Double 等等listOfNum.add(null);//也是合法的null属于一切类型

在这个例子中,List<? extends Number>表示列表可以持有Number类型或其子类型(如IntegerDouble等)的对象,但你不能往这个列表中添加除了null之外的任何元素,因为编译器不知道列表的确切类型

泛型中常见的类型参数

  • T:表示任意类型,是Type的缩写,常用于泛型类,方法,接口中
  • K,V:分别表示键(key)和值(value),常用于键值对中,如Map<K,V>
  • E:表示元素(Element),常用于集合中如List<E>
  • N:表示数字(Number),常用于数字类型
  • S, U, V等:用于表示第二、第三、第四个泛型类型参数,这些字母在代码中的使用已成为一种约定俗成的规范

集合

java中,集合框架是一组接口和类的集合,他们提供了一种数据储存和操作的方式.java的集合框架主要包括两大接口CollectionMap

Collection接口

  • Collection是所有单列集合的根接口,其子接口包括List,Set,Queue

    java.util.Collection下的接口和继承类关系简易结构图:

java.util.Map下的接口和继承类关系简易结构图:

List接口

List集合也被称为序列,其允许有重复的元素.List接口的实现类主要有ArrayList, LinkedList Vector

ArrayList

底层使用数组实现,不是线程安全,查询速度块,但插入速度慢

 public static void main(String[] args) {//创建ArrayList对象List<String> list=new ArrayList<>();//使用add()方法向数组中添加元素list.add("张三");list.add("李四");list.add("王五");//使用get(index)方法获取数组下标为index的元素System.out.println(list.get(0));//list的增强for循环for (String s : list) {System.out.println(s);}}

LinkArray

底层使用双向链表实现,查询速度慢,但其增删速度快,使用方法与ArrayList基本一致

    public static void main(String[] args) {List<String> list=new LinkedList<>(); //创建LinkedList对象list.add("张三");//一下的使用方法与ArrayList一致list.add("李四");list.add("王五");System.out.println(list.get(0));for (String s : list) {System.out.println(s);}

Vector

底层与ArrayList一致都是使用数组实现的,线程安全性高,但效率较低

public static void main(String[] args) {List<String> list=new Vector<>();//创建Vector对象list.add("张三");list.add("李四");list.add("王五");System.out.println(list.get(0));for (String s : list) {System.out.println(s);}}

Set接口

其特点为无序集合,不允许有重复元素,包括主要实现类HashSet,LinkedSetTreeSet

HashSet

作为较为常用的Set集合,其底层是基于哈希表实现的,这就决定了其无法添加重复的元素和无序性

  • HashSet之所以能保证元素的唯一性是重写了其hashCode()方法和equals()方法,具体操作为:
  1. HashSet在每次储存元素的过程都会首先查看其hashCode()值,看其哈希值是否与以存入HashSet的元素的哈希值一致,若不一致则直接存入集合,若一致则进行步骤2
  2. 如果其哈希值相同则继续调用元素的equals()方法与哈希值相同的元素进行依次比较,若返回值为ture,则说明重复则不添加,反之则添加
  • 无序性:HashSet 是基于哈希表实现的,因此在添加元素时,不会按照其添加的顺序排放,而是根据哈希表原理,通过hash值存放.
  • 遍历无需性:当使用迭代器或者增强for循环时,HashSet的遍历也不是按照其元素插入的顺序执行的,也不是按照任何可预测的顺序执行的,而是基于哈希表的内部结构决定的,则意味着对于相同的HashSet ,在不同的JVM和实现方法下其遍历顺序都是不同的
 HashSet<Integer> integerHashSet = new HashSet<>(); //创建HashSet对象integerHashSet.add(1);integerHashSet.add(1);//使用add方法向其插入元素integerHashSet.add(2);integerHashSet.add(-1);for (Integer integer : integerHashSet) {System.out.println(integer);} //打印结果为 -1 1 2

LinkedHashSet

作为HashSet的子类,继承了HashSet的所有特性,即不允许集合中有重复元素,但与HashSet不同的是LinkedHashSet内部维护了一个双向链表,用于实现按元素的插入的顺序实现遍历

  • 底层数据逻辑:LinkedHashSet底层的数据结构包括一个数组和一个双向链表(或者是红黑树),这个数组和双向链表(或者红黑树)共同构成了LinkedHashMap (本文将在下文讲解到),的实现基础,LinkedHashSet就是通过封装LinkedHashMap来实现其功能,即底层是基于LinkedHashMap实现的
  • 具体实现: LinkedHashSet,在添加元素时,都会调用LinkedHashMapput方法来实现.LinkedHashMap 的put方法首先会计算插入元素的哈希值,并根据哈希值确定元素在数组中的位置,然后,会在双向链表(或红黑树)添加一个节点,保存元素值,因此每次遍历*LinkedHashSet时实际上是遍历其双向链表(红黑树)*,从而保证了遍历顺序与元素插入顺序一致
 LinkedHashSet<Integer> integerLinkedHashSet = new LinkedHashSet<>();//创建一个LinkedHashSet对象integerLinkedHashSet.add(1);integerLinkedHashSet.add(1);//添加元素integerLinkedHashSet.add(2);integerLinkedHashSet.add(-1);for (Integer integer : integerLinkedHashSet) {System.out.println(integer);}//打印结果与插入顺序一致 1 2 -1

TreeSet

TreeSet 是Set的子类,因此也保留的Set接口的特性,特别的是TreeSet是基于红黑树实现的

  • 底层数据逻辑:TreeSet 的底层实际上是基于TreeMap 作为底层存储实现的,TreeSet内部维护了一个NavigableMap (实际上就是TreeMap的一个实例化对象),用于存储元素,在这个映射中,键(key)就是TreeSet中的元素,而值(value)是一个固定的关系共享的Object对象,(在TreeSet中,这个Object对象被命名为PRESENT),用于表现值的存在性,不储存特点的值.

以下是TreeSet内部代码结构:

Untitled

  • TreeSet的排序机制:

TreeSet元素默认是根据自然顺序或根据指定的Comparator进行排序,如果没有提供Comparator则,TreeSet会按照元素自然排序;如果提供了Comparator则使用Comparator来确定元素的顺序

public class NumComparator implements Comparator<Integer> {
//NumComparator类实现了Comparator接口@Override//重写了compare方法public int compare(Integer o1, Integer o2) {return Integer.compare(o1,o2);}
} TreeSet<Integer> integerTreeSet = new TreeSet<>(new NumComparator());//传入NumComparator对象表明该TreeSet以该方式排序元素integerTreeSet.add(1);//添加元素integerTreeSet.add(-1);integerTreeSet.add(2);for (Integer integer : integerTreeSet) {System.out.println(integer);}打印结果为[-1,1,2]

List与Set的常用方法

返回值类型 方法和描述
boolean add(E e) 将指定的元素追加到此列表的末尾。
void add(int index, E element) 在此列表中的指定位置插入指定的元素。
boolean addAll(Collection<? extends E> c) 按指定集合的Iterator返回的顺序将指定集合中的所有元素追加到此列表的末尾。
boolean addAll(int index, Collection<? extends E> c) 将指定集合中的所有元素插入到此列表中,从指定的位置开始。
void clear() 从列表中删除所有元素。
boolean contains(Object o) 如果此列表包含指定的元素,则返回 true 。
E get(int index) 返回此列表中指定位置的元素。
int indexOf(Object o) 返回此列表中指定元素的第一次出现的索引,如果此列表不包含元素,则返回-1。
boolean isEmpty() 如果此列表不包含元素,则返回 true 。
Iterator iterator() 以正确的顺序返回该列表中的元素的迭代器。
int lastIndexOf(Object o) 返回此列表中指定元素的最后一次出现的索引,如果此列表不包含元素,则返回-1。
E remove(int index) 删除该列表中指定位置的元素。
boolean remove(Object o) 从列表中删除指定元素的第一个出现(如果存在)。
boolean removeAll(Collection<?> c) 从此列表中删除指定集合中包含的所有元素。
E set(int index, E element) 用指定的元素替换此列表中指定位置的元素。
int size() 返回此列表中的元素数。
Object[] toArray() 以正确的顺序(从第一个到最后一个元素)返回一个包含此列表中所有元素的数组。

Map(字典)

Map是一种将键(key)映射到值(value)的对象,它提供了一种键值对的储存机制,其中每个键都唯一映射到一个值,这种结构有利于快速查找,插入和删除值

Map的存储结构:

Untitled

HashMap

HashMap是基于哈希表实现的,它允许使用null键和null值,HashMap不保证映射的顺序,即遍历Map时元素的顺序可能与插入顺序不同,HashMap底层主要维护一个数组和一个链表

  • HashMap的底层原理:
  1. HashMap底层维护了一个数组,被称为”桶”,用来储存多个键值对,没有指定初始量时,数组默认长度是16
  2. 当插入数据时两个不同的键产生了哈希冲突,这时就会通过HashMap底层维护的链表来解决哈希冲突
 HashMap<Integer, String> integerStringHashMap = new HashMap<>();//创建HashMap对象integerStringHashMap.put(1,"one");//Map使用put添加元素integerStringHashMap.put(-1,"-one");integerStringHashMap.put(2,"two");for (Map.Entry<Integer, String> entry : integerStringHashMap.entrySet()) {System.out.println(entry.getKey()+" "+entry.getValue());}//对于Map有特殊的遍历方式,本文将会在下文解析//输出[-1 -one,1 one,2 two]

TreeMap

TreeMap是基于红黑树实现的Map接口,基于这种数据结构让TreeMap 可以在log(n)时间复杂度完成containsKey、get、put和remove等操作.TreeMap是实现TreeSet的基础

  • 有序性:由于基于红黑树实现储存,则保证了TreeMap是有序的,这种有序可以是自然顺序(即插入顺序),或者可以根据指定Comparator实现
TreeMap<Integer, String> integerStringHashMap = new TreeMap<>();//创建TreeMap对象integerStringHashMap.put(1,"one");//Map使用put添加元素integerStringHashMap.put(-1,"-one");integerStringHashMap.put(2,"two");for (Map.Entry<Integer, String> entry : integerStringHashMap.entrySet()) {System.out.println(entry.getKey()+" "+entry.getValue());}//对于Map有特殊的遍历方式,本文将会在下文解析//输出[-1 -one,1 one,2 two]

HashTable

HashTable底层原理与HashMap十分相似,但与HashMap相比HashTable的put,get,remove 加上了同步块,和使用了this锁,则使得HashTable线程是安全的,但性能较低

  • 键和值的约束:HashTable是不允许使用null作为键和值的,否则会报出NullPointerException 异常
      HashMap<Integer, String> integerStringHashMap = new HashMap<>();//创建HashMap对象integerStringHashMap.put(1,"one");//Map使用put添加元素integerStringHashMap.put(-1,"-one");integerStringHashMap.put(2,"two");for (Map.Entry<Integer, String> entry : integerStringHashMap.entrySet()) {System.out.println(entry.getKey()+" "+entry.getValue());}//对于Map有特殊的遍历方式,本文将会在下文解析//输出[-1 -one,1 one,2 two]

LinkedHashMap

LinkedHashMap继承了HashMap ,Linked的内部维护了一个双向链表用于保证元素的顺序

  • LinkedHashMap内部结构:其内部结合了哈希表和双向链表两种数据结构,哈希表用于快速检索元素,双向链表用于维护元素的顺序
  • 插入和访问:当元素被插入LinkedHashMap时,会在链表的尾部添加一个新的节点。如果设置了按访问顺序排列(通过构造函数或setAccessOrder方法),则每次访问元素时,会将该节点移动到链表的尾部,以保持访问顺序
LinkedHashMap<Integer, String> integerStringHashMap = new LinkedHashMap<>();//创建LinkedHashMap对象integerStringHashMap.put(1,"one");//Map使用put添加元素integerStringHashMap.put(-1,"-one");integerStringHashMap.put(2,"two");for (Map.Entry<Integer, String> entry : integerStringHashMap.entrySet()) {System.out.println(entry.getKey()+" "+entry.getValue());}//对于Map有特殊的遍历方式,本文将会在下文解析//输出[1 one,-1 -one,2 two]

Map的遍历方式

由于Map数据结构的特性,(使用键值对),因此必须指定要遍历的条件,例如按键或按值遍历等等

  • 使用entrySet()和增强for循环:

    通过entrySet()方法,Map可以被转换为一个包含Map.Entry对象的Set集合,其中每个Map.Entry对象都代表Map中的一个键值对。然后,可以使用增强for循环来遍历这个Set集合

      LinkedHashMap<Integer, String> integerStringHashMap = new LinkedHashMap<>();//创建LinkedHashMap对象for (Map.Entry<Integer, String> entry : integerStringHashMap.entrySet()) {System.out.println(entry.getKey()+" "+entry.getValue());}
    
  • 使用KeySet()和增强for循环:

    如果只对Map的键感兴趣,可以使用keySet()方法获取一个包含Map中所有键的Set集合,然后遍历这个集合。如果需要获取对应的值,可以通过键来从Map中获取。

     LinkedHashMap<Integer, String> integerStringHashMap = new LinkedHashMap<>();for (Integer integer : integerStringHashMap.keySet()) {//其中integer表示Map的键值//通过Map方法的get(key)方法返回的是通过key映射的valueSystem.out.println(integer+integerStringHashMap.get(integer));}
  • 使用values()和增强for循环:

    KeySet()方法同理,如果只对Map的值感兴趣,可以使用values()方法获取一个包含Map中所有值的Collection集合,然后遍历这个集合。但请注意,这种方式无法直接获取到对应的键。只能获取其value值

     LinkedHashMap<Integer, String> integerStringHashMap = new LinkedHashMap<>();//创建LinkedHashMap对象for (String value : integerStringHashMap.values()) {System.out.println(value);}
    
  • 使用entrySet()Iterator迭代器

    使用 entrySet() 方法结合 Iterator 迭代器来遍历 Map 中的键值对是一种常见的做法,尤其当需要同时访问键和值时,整体是通过while循环实现的

    • 在使用前必须使用interator()方法构建一个interator 对象,并且需要通过 Iterator 的 hasNext() 方法检查是否还有下一个元素。

    • 使用 Iterator 的 next() 方法获取下一个 Map.Entry 对象,从 Map.Entry 对象中使用 getKey() 和 getValue() 方法分别获取键和值。

              LinkedHashMap<Integer, String> integerStringHashMap = new LinkedHashMap<>();Iterator<Map.Entry<Integer, String>> iterator = integerStringHashMap.entrySet().iterator();//使用interator()创建一个intertor对象这步其实为联合方法可以分为一下两步while (iterator.hasNext()){Map.Entry<Integer, String> entry = iterator.next();//每次通过next()方法获取entries的下一个实体 储存再entry中Integer key=entry.getKey();//使用迭代器的getKey()方法可以获取键String value=entry.getValue();//getValue()方法可以获取值System.out.println(key+value);}
      

      Iterator<Map.Entry<Integer, String>> iterator =integerStringHashMap.entrySet().iterator();
      //使用interator()创建一个intertor对象这步其实为联合方法可以分为一下两步

      1. 先使用entrySet()方法创建一个Set集合:

      Set<Map.Entry<Integer, String>> entries = integerStringHashMap.entrySet();

      其中Map.Entry<>表示Map中的一个实体

      1. 再使用interator()构造一个interator对象

      Iterator<Map.Entry<Integer, String>> iterator = entries. Iterator();

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/741205.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

创建vue2项目执行npm install -g @vue/cli报错 no such file or directory, mkdir \\?

第一步:查看默认全局安装路径。 指令:npm config get prefix 我这里路径 npm config get prefix E:\NVM\nvm\node_global 第二步:不存在这个路劲进行更换 npm config set prefix "D:\Develop\nodejs" nodejs里面有 node_cache node_global node_modules 这些文件 …

NAND Flash基础介绍 一

Chip CI 颗粒厂商提供的是整个一块晶圆,晶圆上已经把芯片划分好了,如下图模组厂商或者颗粒厂商会把晶圆拿去分割,拆分出一个个芯片,每个拆分下来的芯片型号、工艺、空间大小等由颗粒厂家提前定制好了,具体说明可参考https://www.cnblogs.com/studywithallofyou/p/18291633…

各类插件

生成图片html2canvas : 基于页面中存在且可用的DOM结构、样式,构建“截图”,绘制到canvas画布中dom-to-image:将DOM 节点序列化为XML,包装到SVG中,再转为图片。

性能测试:性能测试流程与方法

简介 性能测试流程是指在进行性能测试时所遵循的一系列步骤和阶段,以确保对系统的全面测试和评估。性能测试流程的具体步骤可能会因组织、项目和测试需求而有所不同。 性能****测试流程分析现状:首先需要对应用程序或系统进行详细的分析,了解其当前的性能状况、发现性能瓶颈…

ESP32-S3开发板技术研究第一篇

1. 最小系统板原理图框架,第1路USB转串口,第2路USB直连模组 2. RGB灯是自带主控的,WS2812是一种智能控制LED灯源,集成了控制电路和RGB芯片在一个5050封装组件中。 3. 软件开发,编译环境 3.1 先安装 vs code 3.2 在vscode 的扩展里面搜索 ESP-IDF插件,安装

Flask API 如何接入 i18n 实现国际化多语言

如何在现有的 Flask API 项目中,引入 i18n 实现国际化多语言,同时与前端 Vue3 页面联动。​ 1. 介绍 上一篇文章分享了 Vue3 如何如何接入 i18n 实现国际化多语言,这里继续和大家分享 Flask 后端如何接入 i18n 实现国际化多语言。 用户请求 API 的多语言化其实有两种解决方案…

1 python介绍、基本语法、流程控制

一、 Python介绍python的创始人为吉多范罗苏姆(Guido van Rossum)。1989年的圣诞节期间,吉多范罗苏姆为了在阿姆斯特丹打发时间,决心开发一个新的脚本解释程序,作为ABC语言的一种继承。 最新的TIOBE排行榜,Python赶超PHP占据第五, Python崇尚优美、清晰、简单,是一个优…

记录 中**信 ruoyi项目 部署全流程

零 本地环境改为线上环境 包括 1 后端的数据库连接地址 2 后端的文件存储本地地址 3 后端的文件存储ip地址 4 前端baseUrl 一 后端项目打包双击package 二 mstsc 进入服务器 三 备份四 后端jar包替换五 前端文件替换六 检查数据库需不需要备份 七 启动 java -jar ruoyi-admin.j…

hackmyvm-airbind

环境 靶机ip:未知 攻击机kali:ip1:192.168.96.59 ip2:192.168.56.103 主机探测 arp-scan -l发现了56.104这个机子应该就是我们的靶机,接下来先对其进行端口扫描 端口扫描发现开放了80端口,但是22端口状态显示为filtered,不知道开放还是关闭 访问80端口,发现是wallos的管理…

Vue3-slot

描述为了能在当前组件中使用其他组件中的内容,并且可以改变其他组件中的内容结构。使用的技术就叫做“插槽”。在某些场景中,我们可能想要为子组件传递一些模板片段,让子组件在它们的组件中渲染这些片段。举例:子组件<FancyButton/>: <template><button cl…

FPGA bit转bin文件

首先科普一下 什么是bitstream文件 FPGA比特流(bitstream)是一种用于配置可编程逻辑器件的数据格式,特别是现场可编程门阵列(FPGA)。比特流包含了硬件逻辑电路、路由信息以及芯片上寄存器和存储器(如查找表LUT)的初始值。通常认为比特流具有厂商特定的格式,因此很难反向…

mysql集群高可用架构MHA

一、MHA概述 1.为什么要用MHA Master的单点故障问题 2.什么是 MHA MHA(Master High Availability)是一套优秀的MySQL高可用环境下故障切换和主从复制的软件。MHA 的出现就是解决MySQL 单点的问题。MySQL故障切换过程中,MHA能做到0-30秒内自动完成故障切换操作。MHA能在故障切…