集合框架(java学习总结笔记)
集合框架和Iterator接口
简介
集合框架:用于存储数据的容器。
集合框架是为表示和操作集合而规定的一种统一的标准的体系结构。
集合接口与实现分离
接口:表示集合的抽象数据类型。接口允许我们操作集合时不必关注具体实现,从而达到“多态”。
实现:集合接口的具体实现,是重用性很高的数据结构。
特点
- 可以动态保存任意多个对象,使用比较方便
- 提供了一系列操作对象的方法: add, remove, set, get
集合和数组的区别
- 数组是固定长度的;集合可变长度的。
- 数组可以存储基本数据类型,也可以存储引用数据类型;集合只能存储引用数据类型。
- 数组存储的元素必须是同一个数据类型;集合存储的对象可以是不同数据类型。
数据结构:就是容器中存储数据的方式。
对于集合容器,有很多种。因为每一个容器的自身特点不同,其实原理在于每个容器的内部数据结构不同。
使用集合框架的好处
- 容量自增长;
- 提供了高性能的数据结构和算法,使编码更轻松,提高了程序速度和质量;
- 允许不同 API 之间的互操作,API之间可以来回传递集合;
- 可以方便地扩展或改写集合,提高代码复用性和可操作性。
- 通过使用JDK自带的集合类,可以降低代码维护和学习新API成本。
集合的框架体系
集合框架中的接口:
集合有两个基本接口: Collection 和 Map, Collection和Map是Java集合框架的根接口,这两个接口又包含了一些子接口或实现类。
- Collection一次存一个元素,是单列集合;
- Map一次存一对元素,是双列集合。Map存储的一对元素:键–值,键(key)与值(value)间有对应(映射)关系。
Iterator接口
Iterator用于集合元素的遍历的接口
Iterator接口拥有4个方法:
public interface Iterator<E>{//返回迭代中的下一个元素.如果已经到达了集合的末尾,将抛出一个NoSuchElementException的异常E next();//如果存在下一个可访问的元素,则返回true;boolean hasNext();//删除上一次访问的对象.这个方法必选紧跟在访问一个元素之后执行.若果上次访问之后集合已经发生了变化,//这个方法将抛出一个IllegalStateExceptionvoid remove();//对每个剩余元素执行给定的操作,直到所有元素都被处理或动作引发异常。 //如果指定了该顺序,则按迭代的顺序执行操作。 动作抛出的异常被转发给呼叫者。default void forEachRemaining(Consumer <? super E> action);
}
使用迭代器遍历集合元素
public class Test {public static void main(String[] args) {Collection c = new ArrayList();c.add("a01");c.add("a02");c.add("a03");System.out.println(c);//hasNext()保证next在到达集合末尾时停止运行 快捷键ititIterator iter = c.iterator();while (iter.hasNext()) {String s = (String) iter.next();System.out.println(s);}//foreach循环同样可以进行循环操作//编译器简单地将foreach循环转换成带有迭代器的循环//foreach循环可以处理任何实现了Iterable接口的对象for (Object o :c) {System.out.println(o);}}
}
对于java迭代器我们可以认为其位于两个元素之间.当调用next时,迭代器就越过下一个元素,并返回刚刚越过的那个元素的引用.
使用Iterator迭代器进行删除集合元素
Iterator iter = c.iterator();iter.next();iter.remove();
remove方法对next方法具有依赖性,调用remove前没有调用next僵尸不合法的,会抛出IllegalStateException
在执行remove操作时,同样先执行checkForComodification(),如果不相等会抛出ConcurrentModificationException异常,相等则会执行ArrayList的remove()方法,该方法会将modCount值加1,将expectedModCount=modCount,使之保持统一.
ListIterator接口
Iterator中没有add方法,提供了一个子接口ListIterator,只能用于各种List类型的访问.
常用API:
Modifier and Type | Method and Description |
---|---|
void
|
add(E e) 将指定的元素插入列表(可选操作)。
|
boolean
|
hasNext() 返回 true 如果遍历正向列表,列表迭代器有多个元素。
|
boolean
|
hasPrevious() 返回 true 如果遍历反向列表,列表迭代器有多个元素。
|
E
|
next() 返回列表中的下一个元素,并且前进光标位置。
|
int
|
nextIndex() 返回随后调用 next() 返回的元素的索引。
|
E
|
previous() 返回列表中的上一个元素,并向后移动光标位置。
|
int
|
previousIndex() 返回由后续调用 previous() 返回的元素的索引。
|
void
|
remove() 从列表中删除由 next() 或 previous() 返回的最后一个元素(可选操作)。
|
void
|
set(E e) 用 指定的元素替换由 next() 或 previous() 返回的最后一个元素(可选操作)。
|
Collection接口
Collection集合主要有List和Set两大接口, 他们实现的子类都是单列集合;
Collection接口没有直接实现的子类,是通过它的子接口Set和List来实现的;
- List:有序(元素存入集合的顺序和取出的顺序一致),元素都有索引。元素可以重复。
- Set:无序(存入和取出顺序有可能不一致),不可以存储重复元素。必须保证元素唯一性。
List集合
List是元素有序并且可以重复的集合.
List中的每个元素拥有对应的整数型序号记载其在容器中的位置,支持通过序号存取容器中的元素.
List的主要实现:ArrayList, LinkedList, Vector.
List常用方法:
ArrayList、LinkedList、Vector 的区别
ArrayList | LinkedList | vector | |
---|---|---|---|
底层实现 | 数组 | 双向链表 | 数组 |
同步性及效率 | 不同步,非线程安全,效率高,支持随机访问 | 不同步,非线程安全,效率高 | 同步,线程安全,效率低 |
特点 | 查询快,增删慢 | 查询慢,增删快 | 查询快,增删慢 |
默认容量 | 10 | / | 10 |
扩容机制 | int newCapacity = oldCapacity + (oldCapacity >> 1);//1.5 倍 | / | 2 倍 |
- LinkedList 不会出现扩容的问题,所以比较适合随机位置增、删。但是其基于链表实现,所以在定位时需要线性扫描,效率比较低。
- 当操作是在一列数据的后面添加数据而不是在前面或中间,并且需要随机地访问其中的元素时,使用ArrayList会提供比较好的性能;
- 当你的操作是在一列数据的前面或中间添加或删除数据,并且按照顺序访问其中的元素时,就应该使用LinkedList了。
通过Iterator遍历并操作元素
List可以通过for循环,增强for循环(foreach)以及迭代器进行遍历
public static void main(String[] args) {List a = new ArrayList();a.add("Amy");a.add("Carl");a.add("Erica");List b = new ArrayList();b.add("Bob");b.add("Doug");b.add("Frances");b.add("Gloria");ListIterator aIter = a.listIterator();Iterator bIter = b.iterator();//将b中的元素加入a中while (bIter.hasNext()) {if (aIter.hasNext()){aIter.next();}aIter.add(bIter.next());}System.out.println(a);//ABCDEFG//重置迭代器bIter = b.iterator();//迭代器每移动两次删除前一次的next返回的元素while (bIter.hasNext()) {bIter.next();if (bIter.hasNext()){bIter.next();bIter.remove();}}//foreach遍历for (Object o :b) {System.out.println(o);//BF}a.removeAll(b);//for循环遍历for (int i = 0; i < a.size(); i++) {System.out.println(a.get(i));//ACDEG}
}
对于ArrayList底层结构为数组的集合来说,在进行遍历并操作元素时尽量采用迭代器进行操作.
for循环遍历在集合元素发生变化时索引值变化引发的一些问题(连续重复值的删除导致漏删)
foreach循环为简化版迭代器,在使用时会调用迭代器的next方法,其中checkForComodification()方法对expectedModCount和modCount两值进行判断.modCount是指集合的修改次数,当进行add或者delete时,modCount会+1;expectedModCount是指集合的迭代器的版本号,初始值是modCount,但是当集合进行add或者delete操作时,modCount会+1,而expectedModCount不会改变.迭代器中的remove方法会将modCount的值赋予expectedModCount,使两值保持同步,不会抛出异常.
Set集合
Set集合元素无序(存入和取出的顺序不一定一致),没有索引, 并且没有重复对象。
Set的主要实现类:HashSet, TreeSet。
Set的常用方法
其中因为没有索引,所以没有get和set方法,hashCode是Set中的一个重要方法,在Set的底层实现中起到了至关重要的作用.
HashSet、TreeSet、LinkedHashSet的区别
HashSet | TreeSet | LinkedHashSet | |
---|---|---|---|
底层实现 | HashMap(哈希表即数组,链表和红黑树) | 红黑树 | LinkedHashMap(维护数组和双向链表) |
重复性 | 不允许重复 | 不允许重复 | 不允许重复 |
有无序 | 无序 | 有序,支持两种排序方式,自然排序和定制排序,其中自然排序为默认的排序方式。 | 有序,插入和取出顺序一致 |
时间复杂度 | add(),remove(),contains()方法的时间复杂度是O(1) | add(),remove(),contains()方法的 时间复杂度是O(logn) | LinkedHashSet在迭代访问Set中的全部元素时,性能比HashSet好,但是插入时性能稍微逊色于HashSet,时间复杂度是 O(1)。 |
同步性 | 不同步,线程不安全 | 不同步,线程不安全 | 不同步,线程不安全 |
null值 | 允许null | 不支持null值,会抛出 java.lang.NullPointerException 异常。因为TreeSet应用 compareTo() 方法于各个元素来比较他们,当比较null值时会抛出 NullPointerException异常。 | 允许null值 |
比较 | equals() | compareTo() | equals() |
Map接口
- Map与Collection并列存在,用于保存具有映射关系的数据: Key-Value
- Map中的Key和Value可以是任何引用类型数据,会封装到HashMap$Node对象中
- Map中的Key不允许重复,Value可以重复
- Key可以为null,Value也可以为null,但是须遵守第三条
- Key和Value是单向的一对一关系(Key==>Value),指定Key一定能找到对应的Value
- Map 的常用实现类:HashMap、TreeMap、HashTable、LinkedHashMap
Map接口常用方法
HashMap、HashTable、TreeMap的区别
HashMap | HasTable | TreeMap | |
---|---|---|---|
底层实现 | 哈希表 | 哈希表 | 红黑树 |
同步性 | 不同步 | 同步 | 不同步 |
null值 | 键值对可以为null,但是Key值只能有一个null | 不允许key、value 是 null |
value允许为null。 当未实现 Comparator 接口时,key 不可以为null 当实现 Comparator 接口时,若未对 null 情况进行判断,则可能抛 NullPointerException 异常。如果针对null情况实现了,可以存入,但是却不能正常使用get()访问,只能通过遍历去访问。 |
hash | 使用hash(Object key)扰动函数对 key 的 hashCode 进行扰动后作为 hash 值 | 直接使用 key 的 hashCode() 返回值作为 hash 值 | |
容量 | 容量为 2^4 且容量一定是 2^n | 默认容量是11,不一定是 2^n | |
扩容 | 两倍,且哈希桶的下标使用 &运算代替了取模 | 2倍+1,取哈希桶下标是直接用模运算 |
HashMap
- Hashmap接口基于哈希表的实现,是使用频率最高的用于键值对处理的数据类型。
- 它根据键的hashCode值存储数据,大多数情况下可以直接定位到它的值,特点是访问速度快,遍历顺序不确定,线程不安全
- 可以用 Collections的synchronizedMap方法使HashMap具有线程安全的能力,或者使用ConcurrentHashMap类。
HashTable
- Hashtable和HashMap从存储结构和实现来讲有很多相似之处,不同的是它承自Dictionary类,而且是线程安全的,另外Hashtable不允许key和value为null,并发性不如ConcurrentHashMap。
- Hashtable不建议在新代码中使用,不需要线程安全的场合可以用HashMap替换,需要线程安全的场合可以用ConcurrentHashMap替换。
LinkedHashMap
- LinkedHashMap继承了HashMap,是Map接口的哈希表和链接列表实现,它维护着一个双重链接列表,此链接列表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序。
TreeMap
- TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序(自然顺序),也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。
HashMap在JDK1.7和JDK1.8中有哪些不同
不同 | JDK1.7 | JDK1.8 |
---|---|---|
存储结构 | 数组+链表 | 数组+链表+红黑树 |
初始化方式 |
单独函数: inflatable()
|
集成到了扩容函数: resize()
|
hash值计算方式 | 扰动处理 = 9次扰动 = 4次位运算 + 5次异或运算 | 扰动处理 = 2次扰动 = 1次位运算 + 1次异或运算 |
存放数据的规则 | 无冲突时,存放数组;冲突时,存放链表 | 无冲突时,存放数组;冲突 & 链表长度 < 8:存放单链表;冲突 & 链表长度 > 8&数组长度达到64:树化并存放红黑树 |
插入数据方式 | 头插法(先讲原位置的数据移到后1位,再插入数据到该位置) | 尾插法(直接插入到链表尾部/红黑树) |
扩容后存储位置的计算方式 | 全部按照原来方法进行计算(即hashCode ->> 扰动函数 ->> (h&length-1)) | 按照扩容后的规律计算(即扩容后的位置=原位置 or 原位置 + 旧容量) |
Collections工具类
Collections是一个集合工具类,方便对集合的操作,提供了一些系列的静态方法
静态方法演示:
public class Test {public static void main(String[] args) {List list = new ArrayList();list.add("a01");list.add("c02");list.add("k03");list.add("s04");list.add("e091");//逆向反转排序Collections.reverse(list);//随机排序Collections.shuffle(list);//根据元素的自然顺序对List元素升序排序Collections.sort(list);//交换对应位置元素Collections.swap(list,2,0);//根据指定的 Comparator 产生的顺序对 List 集合元素进行排序//按照元素的第三个字符的自然顺序降序Collections.sort(list, new Comparator() {@Overridepublic int compare(Object o1, Object o2) {return ((String)o2).charAt(2) - ((String)o1).charAt(2);}});//max方法默认返回最大的元素//此处是根据 Comparator 返回长度最大的元素System.out.println(Collections.max(list, new Comparator<Object>() {@Overridepublic int compare(Object o1, Object o2) {return ((String) o1).length() - ((String) o2).length();}}));//复制List到指定DestListList destlist = new ArrayList();//创建了一个和List大小相同的空数组,保证可以全部复制for (int i = 0; i < list.size(); i++) {destlist.add("");}Collections.copy(destlist,list);}/*将非同步集合转成同步集合的方法:Collections中的 XXX synchronizedXXX(XXX); 原理:定义一个类,将集合所有的方法加同一把锁后返回。List synchronizedList(list);Map synchronizedMap(map);*/
}
Arrays数组工具类
用于操作数组对象的工具类,里面都是静态方法。
数组 -> 集合:asList方法,将数组转换成list集合。
String[] arr ={"abc","kk","qq"};
List<String> list =Arrays.asList(arr);//将arr数组转成list集合。
- 可以通过list集合中的方法来操作数组中的元素:isEmpty()、contains、indexOf、set;
- 注意(局限性):数组是固定长度,不可以使用集合对象增加或者删除等,会改变数组长度的功能方法。比如add、remove、clear。(会报不支持操作异常UnsupportedOperationException);
- 如果数组中存储的引用数据类型,直接作为集合的元素可以直接用集合方法操作。
- 如果数组中存储的是基本数据类型,asList会将数组实体作为集合元素存在。
集合 -> 数组:用的是Collection接口中的toArray()方法;
List list = new ArrayList();
Object[] array = list.toArray();
- 如果给toArray传递的指定类型的数据长度小于了集合的size,那么toArray方法,会自定再创建一个该类型的数据,长度为集合的size。
- 如果传递的指定的类型的数组的长度大于了集合的size,那么toArray方法,就不会创建新数组,直接使用该数组即可,并将集合中的元素存储到数组中,其他为存储元素的位置默认值null。
- 所以,在传递指定类型数组时,最好的方式就是指定的长度和size相等的数组。
用基本数据类型的数组转换ArrayList,ArrayList的size有问题
public class Test {public static void main(String[] args) {int[] a = {1,2,6,9,5};List ints = Arrays.asList(a);System.out.println("size:"+ints.size());//size:1Integer[] b = {1,2,6,9,5};List ints1 = Arrays.asList(b);System.out.println("size:"+ints1.size());//size:5}
}
asList方法接受的参数是一个泛型的变长参数,我们知道基本数据类型是无法泛型化的,也就是说基本类型是无法作为asList方法的参数的, 要想作为泛型参数就必须使用其所对应的包装类型。但是这个这个实例中为什么没有出错呢?因为该实例是将int 类型的数组当做其参数,而在Java中数组是一个对象,它是可以泛型化的。所以该例子是不会产生错误的。既然例子是将整个int 类型的数组当做泛型参数,那么经过asList转换就只有一个int 的列表了.
asList转换得到的ArrayList不是java.util.ArrayList
Integer[] b = {1,2,6,9,5};
List list = Arrays.asList(b);
//添加元素时抛出异常UnsupportedOperationException
list.add(10);
此处ArrayList是Arrays的内部类继承了AbstractList但是没有实现add方法,而是直接抛出UnsupportedOperationException异常.
Integer[] b = {1,2,6,9,5};
List list = new ArrayList(Arrays.asList(b));
//添加元素时抛出异常UnsupportedOperationException
list.add(10);
使用ArrayList将其转为list集合而非之前的Arrays的内部类ArrayList
整体框架图
参考文献
Java集合框架总结
Java集合框架最全详解
Java核心技术卷Ⅰ第九章集合
韩顺平笔记第14章集合
集合框架(java学习总结笔记)相关推荐
- java学习---新手笔记,多多包涵
引用数据类型{ 类, 接口, 数组 } Java局部变量 局部变量声明在方法.构造方法或者语句块中: 局部变量在方法.构造方法.或者语句块被执行的时候创建,当它们执行完成后,变量将会被销毁: 访问修饰 ...
- java super object,java学习记录笔记--继承,super,Object类
继承: Java中的继承是单继承的. 1.子类拥有父类的全部属性和方法. 可是属性和方法的修饰符不能使private. 2.能够复用父类的代码. 方法的重写须要满足的条件: a.返回值类型 b.方法名 ...
- 集合框架|Java集合框架基本使用
大家好,我是程序猿小马,沪漂一族! 写文章就是对于平时的总结以及大家共同学习进步,早日码出各自的梦想
- java 学习心得笔记
j2ee 模式 value object(值对象)用于把数据从某个对象/层传递到其他对 象/层的任意 java 对象. 通常不包含任何业务方法. 也许设计有公共属性,或者提供可以获取属性值的 get ...
- Java 学习多态笔记
一. 多态同一个对象在不同时期表现的出不形态,要构成多态必须满足以下三个前提1.要有继承或实现2.有方法的重写3.有父类的引用指向对象eg:public class Animal {public St ...
- 2022年Java学习笔记目录
一.2022年Java任务驱动课程 任务驱动,统摄知识点:2022年Java程序设计讲课笔记 二.2022年Java学习笔记 (一)踏上Java开发之旅 Java学习笔记1.1.1 搭建Java开发环 ...
- 集合框架学习笔记:Collection体系和Map体系、Collections工具类
集合框架 Java是面向对象编程,万事万物皆"对象",为了方便对"对象"进行操作,需要对"对象"进行存储,而Java集合就是存储" ...
- Java集合与泛型学习笔记
1:Java集合类 首先我们思考为什么出现集合类? 理由: 1面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象操作,就对对象进行储存,集合就是储存对象最常用的一种方式 2 数组和集合同 ...
- Java OOP 7 JAVA 集合框架
Java OOP 第七章 JAVA 集合框架 文章目录 Java OOP 第七章 JAVA 集合框架 一.学习目标 二.数组存在的缺陷 三.Java集合框架 四.Collection接口 五.Coll ...
最新文章
- 序列模式挖掘、频繁项集与频繁序列
- jquery ajax下拉联动,jQuery Ajax MVC 下拉框联动
- SQL Server中查询所有的表、视图、列和存储过程
- pku 3159 Candies 差分约束
- 自定义View -- 刻度尺
- 将list转为json字符串
- 【C++面向对象】类的大小以及虚继承
- Solr学习笔记001---solr在windows下的安装及配置
- 面向对象 —— 类的分类
- php中array_flip数组翻转
- 什么是飞秒激光技术?
- 亿晟科技人脸识别门禁系统方案整体解决办法
- SAP 谈谈存货分析报表
- Python在cmd上打印彩色文字
- 曹胜欢,java那些事儿
- 如何给网站做SEO优化?
- VBA编程——范例一
- 传奇手游单职业服务器外网搭建架设一键端-2023
- 安装Pre-commit Hook npx mrm@2 lint-staged pre-commit不执行
- DAO 的去中心化程度判定:钟形曲线