Java基础之集合篇(内容超详细,带你轻松搞懂List、Set和Map的使用)
1 泛型
1.1 泛型的概念
泛型的本质就是“数据类型的参数化”。我们可以把“泛型”理解为数据类型的一个占位符(形式参数),即告诉编译器,在调用泛型时必须传入实际类型。
例如,下面List中的泛型为Person ,表示List内存储的元素为Person类的对象:
public class TestGerner {public static void main(String[] args) {List<Person> persons = new ArrayList<>();persons.add(new Person("AA",12));persons.add(new Person("BB",12));persons.add(new Person("CC",12));Person person = persons.get(0); //get的返回值就是Person类型}
}
2 Collection接口
2.1 集合架构
• Collection 接口存储一组不唯一,无序的对象
• List 接口存储一组不唯一,有序(索引顺序)的对象
• Set 接口存储一组唯一,无序的对象
• Map接口存储一组键值对象,提供key到value的映射 (Key 唯一 且无序, value 不唯一且无序)
2.2 List
List是有序、可重复的容器。
有序:List中每个元素都有索引标记,可以根据元素的索引标记(在List中的位置)访问元素,从而精确控制这些元素。
可重复: List允许加入重复的元素。更确切地讲,List通常允许满足e1.equals(e2)的元素重复加入容器。
List接口常用的实现类有3个:ArrayList、LinkedList和Vector。
ArrayList是基于数组实现的。特点:查询效率高,增删效率低,线程不安全,长度不受限制。
2.3 Map接口
• 特点 key-value映射
• HashMap: Key无序且唯一 ,Value 无序且不唯一
• LinkedHashMap :有序的HashMap ,速度快
• TreeMap :有序,速度没有HashMap快
•问题:Set和Map有关系吗?
Set和Map采用了相同的数据结构,把Set的集合对象作为Map的key,再使用一个Object常量作为value。
因此:更符合我们说的在Map中,所有的key就是一个Set集合。
Map中常用的方法:
2.3.1 HashMap存储键值对底层过程
HashMap基于哈希表的Map接口实现,是以key-value存储形式存在,即主要用来存放键值对。HashMap 的实现不是同步的,这意味着它不是线程安全的。它的key、value都可以为null。此外,HashMap中的映射不是有序的。
特点:
1.存取无序的
2.键和值位置都可以是null,但是键位置只能是一个null
3.键位置是唯一的,底层的数据结构控制键的
4.jdk1.8前数据结构是:链表 + 数组 jdk1.8之后是 : 链表 + 数组 + 红黑树
5.阈值(边界值) > 8 并且数组长度大于64,才将链表转换为红黑树,变为红黑树的目的是为了高效的查询。
JDK1.8之前的存储方式
在JDK1.8 之前 HashMap 由 数组+链表 数据结构组成的。
数据结构中由数组和链表来实现对数据的存储,它们各有特点。
(1)数组︰占用空间连续。寻址容易,查询速度快。但是,增加和删除效率非常低。
(2)链表︰占用空间不连续。寻址困难,查询速度慢。但是,增加和删除效率非常高。
那么,我们能不能结合数组和链表的优点(即查询快,增删效率也高)呢? 答案就是“哈希表”。
哈希表的本质就是“数组+链表”。
其中Entry[] table就是HashMap的核心数据结构,我们也称之为“位桶数组”。
一个Entry对象存储了:
1.key:键对象value:键值对
2.next:下一个节点
3.hash:键对象的hash值
JDK1.8之后的存储方式
在JDK1.8 之后 HashMap 由 数组+链表 +红黑树数据结构组成的。
如果阈值(边界值) < 8 并且数组长度<64,其存储过程与JDK1.8之前一致。JDK1.8 以后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(或者红黑树的边界值,默认为 8)并且当前数组的长度大于64时,此时此索引位置上的所有数据改为使用红黑树存储。
注意:将链表转换成红黑树前会判断,即使阈值大于8,但是数组长度小于64,此时并不会将链表变为红黑树。而是选择进行数组扩容。
扩容问题:HashMap的位桶数组,初始大小为16。实际使用时,显然大小是可变的。如果位桶数组中的元素达到(0.75*数组length),就重新调整数组大小变为原来2倍大小。扩容很耗时。扩容的本质是定义新的更大的数组,并将旧数组内容挨个拷贝到新数组中。
存储过程:
存储过程的流程图:
说明:
1.size表示 HashMap中K-V的实时数量 , 注意这个不等于数组的长度 。
2.threshold( 临界值) =capacity(容量) * loadFactor( 加载因子 )。这个值是当前已占用数组长度的最大值。size超过这个临界值就重新resize(扩容),扩容后的 HashMap 容量是之前容量的两倍 。
小结:
(1)HashMap中hash函数是怎么实现的?还有哪些hash函数的实现方式?
对于key的hashCode做hash操作,无符号右移16位然后做异或运算。还有平方取中法,伪随机数法和取余数法。这三种效率都比较低。而无符号右移16位异或运算效率是最高的。
(2)当两个对象的hashCode相等时会怎么样?
会产生哈希碰撞,若key值内容相同则替换旧的value.不然连接到链表后面,链表长度超过阈值8就转换为红黑树存储。
(3)何时发生哈希碰撞和什么是哈希碰撞,如何解决哈希碰撞?
只要两个元素的key计算的哈希码值相同就会发生哈希碰撞。jdk8前使用链表解决哈希碰撞。jdk8之后使用链表+红黑树解决哈希碰撞。
(4)如果两个键的hashcode相同,如何存储键值对?
若两个键的hashcode相等,则通过equals()方法比较"key"对象是否相同。
若相同,则新的value覆盖之前的value;若不相同,则将新的键值对添加到哈希表中。
2.3.2 HashMap查找键值对底层过程
取数据过程get(key)
我们需要通过key对象获得“键值对”对象,进而返回value对象。明白了存储数据过程,取数据就比较简单了,参见以下步骤︰
(1)获得key的hashcode,通过hash()散列算法得到hash值,进而定位到数组的位置。
(2)在链表上挨个比较key对象。调用equals()方法,将key对象和链表上所有节点的key对象进行
比较,直到碰到返回true的节点对象为止。
(3)返回equals()为true的节点对象的value对象。
明白了存取数据的过程,我们再来看一下hashcode()和equals方法的关系︰
Java中规定,两个内容相同(equals()为true)的对象必须具有相等的hashCode。因为如果equals()为true而两个对象的hashcode不同;那在整个存储过程中就发生了悖论。
2.3.3 TreeMap
TreeMap是红黑二叉树的经典实现。TreeMap实现了SotredMap接口,它是有序的集合。而且是一个红黑树结构,每个key-value都作为一个红黑树的节点。如果在调用TreeMap的构造函数时没有指定比较器,则根据key执行自然排序。
2.3.4 HashMap与HashTable的区别
(1)HashMap:线程不安全,效率高。允许key或value为null。
(2)HashTable:线程安全,效率低。不允许key或value为null。
3 Set接口
Set接口继承自Collection, Set接口中没有新增方法,方法和Collection保持完全一致。List中的方法,在Set中仍然适用。
Set容器特点∶无序、不可重复。无序指Set中的元素没有索引,我们只能遍历查找;不可重复指不允许加入重复的元素。更确切地讲,新元素如果和Set中某个元素通过equals()方法对比为true,则不能加入;甚至,Set中也只能放入一个null元素,不能多个。
Set常用的实现类有:HashSet、TreeSet等,一般使用HashSet。
3.1 TreeSet的使用
同HashSet的使用大致一样,但是为有序,不可重复。
4 集合的遍历
(1)使用Iterator迭代器遍历容器元素(List/Set/Map)
所有集合类均未提供相应的遍历方法,而是把遍历交给迭代器完成。迭代器为集合而生,专门实现集合遍历。 Iterator是迭代器设计模式的具体实现。
• Iterator中的方法 :
boolean hasNext(): 判断是否存在另一个可访问的元素
Object next(): 返回要访问的下一个元素
void remove(): 删除上次访问返回的对象
(2)使用foreach方法结合Lambda表达式遍历集合(推荐)
4.1 Iterator遍历List
public static void main(String[] args) {testIteraterList();
}
public static void testIteraterList(){List<String> list = new ArrayList<>();list.add("aa");list.add("bb");list.add("cc");//使用Iterater遍历迭代器for (Iterator<String> iter = list.iterator();iter.hasNext();){String temp = iter.next();System.out.println(temp);}
}
4.2 Iterator遍历Map
(1)第一种方式
public static void main(String[] args) {testIteraterList();
}
public static void testIteraterList(){Map<Integer, String> map = new HashMap<>();map.put(100,"aaa");map.put(200,"bbb");map.put(300,"ccc");Set<Map.Entry<Integer, String>> entries = map.entrySet();//使用Iterater遍历迭代器for (Iterator<Map.Entry<Integer, String>> iter = entries.iterator();iter.hasNext();){Map.Entry<Integer, String> entry= iter.next();System.out.println(entry);}
}
(2)第二种方式
Set<Integer> set = map.keySet();
for ( Iterator<Integer> iter = set.iterator();iter.hasNext();){Integer key = iter.next();System.out.println(key+"---"+map.get(key));
}
4.3 foreach遍历集合(以HashMap为例)
HashMap<String,Integer> map = new HashMap<>();map.put("alice",20);map.put("bob",30);map.put("tom",22);map.forEach((k,v)->{ //(k,v)就表示map中存储的键值对System.out.println("key="+k+",value="+v);});
打印结果:
5.集合总结
5.1 集合和数组的比较
数组不是面向对象的,存在明显的缺陷,集合完全弥补了数组的一些缺点,比数组更灵活更实用,可大大提高软件的开发效率而且不同的集合框架类可适用于不同场合。具体如下:
(1) 数组容量固定且无法动态改变,集合类容量动态改变。
(2) 数组能存放基本数据类型和引用数据类型的数据,而集合类中只能放引用数据类型的数据。
(3)数组无法判断其中实际存有多少元素,length只告诉了array容量;集合可以判断实际存有多少元素,而对总的容量不关心。
(4)集合有多种数据结构(顺序表、链表、哈希表、树等)、多种特征(是否有序,是否唯一)、不同适用场合(查询快,便于删除、有序),不像数组仅采用顺序表方式。
(5)集合以类的形式存在,具有封装、继承、多态等类的特性,通过简单的方法和属性调用即可实现各种复杂操作,大大提高软件的开发效率。
5.2 ArrayList和LinkedList 的联系和区别
联系
• 都实现了List接口
• 有序 不唯一(可重复)
ArrayList
• 在内存中分配连续的空间,实现了长度可变的数组
• 优点:遍历元素和随机访问元素的效率比较高
• 缺点:添加和删除需大量移动元素效率低,按照内容查询效率低
LinkedList
• 采用链表存储方式
• 缺点:遍历和随机访问元素效率低下
• 优点:插入、删除元素效率比较高(但是前提也是必须先低效率查询才可。如果插入删除发生在头尾可以减少查询次数)
5.3 Vector和ArrayList的联系和区别
Vector和ArrayList的联系和区别
• 实现原理相同,功能相同,都是长度可变的数组结构,很多情况下可以互用
两者的主要区别如下
• Vector是早期JDK接口,ArrayList是替代Vector的新接口
• Vector线程安全,效率低下;ArrayList重速度轻安全,线程非安全
• 长度需增长时,Vector默认增长一倍,ArrayList增长50%
5.4 HashMap和Hashtable的联系和区别
• 实现原理相同,功能相同,底层都是哈希表结构,查询速度快,在很多情况下可以互用
两者的主要区别如下
• Hashtable是早期JDK提供的接口,HashMap是新版JDK提供的接口
• Hashtable继承Dictionary类,HashMap实现Map接口
• Hashtable线程安全,HashMap线程非安全
• Hashtable不允许null值,HashMap允许null值
5.5 Collection和Collections的区别
• Collection是Java提供的集合接口,存储一组不唯一,无序的对象。它有两个子接口 List和Set。
• Java中还有一个Collections类,专门用来操作集合类 ,它提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。
6 Collections工具类
类java.util.Collections 提供了对Set、List、Map进行排序、填充、查找元素的辅助方法
(1)void sort(List) :对List容器内的元素排序,排序的规则是按照升序进行排序
(2) void shuffle(List) :对List容器内的元素进行随机排列
(3)void reverse(List) :对List容器内的元素进行逆续排列
(4)void fill(List, Object):用一个特定的对象重写整个List容器
(5)int binarySearch(List,object) :对于顺序的List容器,采用折半查找的方法查找特定对象,查找成功则返回该对象所在的位置
7 使用容器存储表格数据
7.1 map和list结合存储整张表
使用Map和List存储表格数据,表格如下:
存储方式
每一行数据使用一个:Map
整个表格使用一个:List
ORM思想:对象关系映射
设计思想:map表示一行数据,多行数据是多个map;将多个map放到list中
public class TestStoreData {public static void main(String[] args) {Map<String, Object> row1 = new HashMap<>();//存储第一行数据row1.put("id", 1001);row1.put("姓名", "张三");row1.put("薪水", 2000);row1.put("入职日期", "2018.5.5");//第二行Map<String, Object> row2 = new HashMap<>();row2.put("id", 1002);row2.put("姓名", "李四");row2.put("薪水", 30000);row2.put("入职日期", "2005.5.5");//第三行Map<String, Object> row3 = new HashMap<>();row3.put("id", 1003);row3.put("姓名", "王五");row3.put("薪水", 3000);row3.put("入职日期", "2020.5.4");List<Map<String, Object>> table1 = new ArrayList<>();table1.add(row1);table1.add(row2);table1.add(row3);for (Map<String, Object> row : table1) {Set<String> keySet = row.keySet();for (String key : keySet){System.out.print(key+":"+row.get(key)+" ");}System.out.println();}}
}
7.2 javaBean和list结合存储整张表
设计思想
每一行数使用一个:javabean对象,整个表格使用一个Map、List。
public class TestStoreData2 {public static void main(String[] args) {User user1 = new User(1001,"张三",20000,"2018.5.5");User user2 = new User(1002,"李四",30000,"2005.4.5");User user3 = new User(1003,"张三",3000,"2020.5.4");//方法一:将user存入listList<User> table = new ArrayList<>();table.add(user1);table.add(user2);table.add(user3);for (User user:table){System.out.print(user.getId()+":"+user.getName()+":"+" "+user.getSalary()+":"+" "+user.getData());System.out.println();}//方法二:将user存入MapMap<Integer,User> map = new HashMap<>();map.put(1,user1);map.put(2,user2);map.put(3,user3);Set<Integer> keySet = map.keySet();for (Integer key:keySet){System.out.print(map.get(key).getId()+" "+map.get(key).getName()+" "+map.get(key).getSalary()+""+map.get(key).getData());System.out.println();}}
}
class User{private int id;private String name;private double salary;private String data;public User() {}public User(int id, String name, double salary, String data) {this.id = id;this.name = name;this.salary = salary;this.data = data;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public double getSalary() {return salary;}public void setSalary(double salary) {this.salary = salary;}public String getData() {return data;}public void setData(String data) {this.data = data;}
}
Java基础之集合篇(内容超详细,带你轻松搞懂List、Set和Map的使用)相关推荐
- java 获取泛型t的class_阿里巴巴都鼎力推荐的java基础之集合其他内容和泛型3
第三节 泛型 3.1为什么需要泛型 没有采用泛型之前 1.不安全:添加元素是无检查 宽进 2.繁琐:获取元素时需要强制类型转换 严出 采用泛型之后 1.安全 严进 2.简单 宽出 3.2什么是泛型ge ...
- java基础之集合篇
1.概述 1.1.什么是集合?有什么用? 数组其实就是一个集合.集合实际上就是一个容器.可以来容纳其它类型的数据.集合为什么说在开发中使用较多?集合是一个容器,是一个载体,可以一次容纳多个对象.在实际 ...
- Java的反射机制,内含超简单实例代码(搞懂反射,这一篇就够了)
一 首先来说说反射机制的概念: 程序在运行时, 对于类来说,可以知道该类的任意属性和方法: 对于对象来说,可以调用该对象的任意方法和属性: 就以上这种动态获取信息的机制就称为Java的反射机制 彻底了 ...
- java 一个大事务下的新增、修改、查询_一文带你轻松搞懂事务隔离级别(图文详解)...
点击上方"linkoffer", 选择关注公众号高薪职位第一时间送达 本文由 SnailClimb 和读者 BugSpeak 共同完成. 事务隔离级别(图文详解) 什么是事务? 事 ...
- Java中的多线程编程(超详细总结)
文章目录 Java中的多线程编程(超详细总结) 一.线程与多线程的概念 二.线程与进程之间的关系 三.一个线程的生命周期 四.多线程的目的和意义 五.线程的实现的方式 Java中的多线程编程(超详细总 ...
- 【Java基础知识回顾篇】之打怪升级Day001
Java基础知识回顾篇之打怪升级Day001 目录 Java基础知识回顾篇之打怪升级Day001 简介 一.为什么现在主流的是Java8和Java11? 二.简单尝试编写java程序 1.编写一个He ...
- Thinking in java基础之集合框架
Thinking in java基础之集合框架 大家都知道我的习惯,先上图说话. 集合简介(容器) 把具有相同性质的一类东西,汇聚成一个整体,就可以称为集合,例如这里有20个苹果,我们把每一个苹果当成 ...
- java基础 ArrayList集合基本方法演示
java基础 ArrayList集合基本方法演示 import java.util.ArrayList; import java.util.Iterator;public class ArrayLis ...
- java把map值放入vector_Thinking in java基础之集合框架
Thinking in java基础之集合框架 大家都知道我的习惯,先上图说话. 集合简介(容器) 把具有相同性质的一类东西,汇聚成一个整体,就可以称为集合,例如这里有20个苹果,我们把每一个苹果当成 ...
- Java Web实现登录注册(超详细附代码)
Java Web实现登录注册(超详细附代码) 文章目录 Java Web实现登录注册(超详细附代码) 1.前言 2.登录注册设计流程 3.注册的数据流程 4.登录的数据流程 5.部分代码的展示 5.1 ...
最新文章
- 拥有“上帝视角”是怎样的体验?高分多模卫星首批影像成果发布
- 面试题整理17 输入一个字符串判断一个字符串是否是有效ip地址
- C/C++写无控制台窗口程序
- Asp.Net Core MVC控制器和视图之间传值
- CentOS 7.0安装Nvidia驱动
- 关于LabVIEW视觉ROI的读取与存储
- 4.1 深层神经网络
- hector与gmapping总结
- 使用crontab定时备份mysql
- 给定一个介于0到1之间的实数(如0.625),打印他的二进制表示
- python使用CV2剪切图片
- Java 抽象类 接口
- 201671030107词频统计软件项目报告
- 非计软专业的学生也能看懂的面向对象编程(《面向对象编程是怎样工作的》平野章/著 读书笔记)
- 网络工程师还吃香吗?
- 群内2018_4月讨论整理2
- 计算机更新时按了关机6,电脑关机后在安装更新怎么办
- 使用UltraEdit删除重复的行
- 低版本android无法连接iPhone手机个人热点问题
- 法国敏捷开发与敏捷测试模式
热门文章
- 迅雷beat下载为php,2019苹果迅雷beat版链接下载地址-迅雷ios内测版ios13企业信任版app下载官方最新beta手机版-迅雷ios永久稳定版西西软件下载...
- 【汉化】nitrosdk。。。这个。。。天朝V5
- ensp的下载与安装
- PDF识别文字、关键字,获取对应坐标,用于插入电子签名
- 安装lamp lnmp 一键安装包网址
- 使用 .reg 文件操作注册表
- 手把手教你用原生js写一个文字提示框
- proteus仿真Arduino
- s3cmd常用命令和使用技巧
- linux系统 如何选择题,Linux考证试题选择题