2021-05-15
集合框架使用集合做什么集合的分类具体的实现类ListArrayListLinkedListSetTreeSetMapHashMapTreeMap数组、List、Set、Map的数据转换数组转 List数组和Set从Map中得到Set和List散列表(哈希表)的基本原理散列表是如何根据Key来快速找到它所匹配的 Value 呢 ?散列表的读写操作写操作(put)读操作(get)IteratorCollections排序查找排序List 集合排序TreeMap 排序自定义类排序外部指定排序本身拥有排序
集合框架
程序开发并不是解决了业务的基本功能就完成了,很多时候程序运行的环境是有限制的。比如内存小,CPU频率低,或者是像手机这样的设备,能源供应有限。在这种环境下,就需要程序能够在有限的环境中提升效率。这就需要使用数据结构和算法。
但是数据结构与算法即便是学过,也未必在工作时能够用好,而且通用性、性能等等也都是问题。加上学习程序开发的受众群体越来越广,让程序员全部自己实现数据结构与算法不是一个好的主意。所以现在很多语言为了能够提升学习效率,降低学习门槛,同时也为了让程序有更好的执行效率和通用性,就自带了各种实现了数据结构与算法的API集合。在Java中,这就是我们现在要学习的「集合框架」
与现在常见到的数据结构类库一样,Java也是将集合类库的接口(interface)与实现(implementation)分离。所以我们的学习方式一般都是先搞明白接口的分类和关系,然后根据不同的接口来学习对应的实现类。
使用集合做什么
搬运数据,集合可以存储数据,然后通过API调用很方便就可以传递大量数据
数据处理,集合中可以直接对数据进行操作,比如统计、去重
排序,可以将数据按照需求进行各种排序,然后再传递给调用者
集合的分类
image-20200212141927241.png
Java的集合从 Collection 接口和 Map 接口入手
Map 接口和 Collection 没有交集,它有自己的方式,只要标准库后缀不是Map 结尾的,都是直接或者间接实现了Collection接口。
Collection 接口中常见的操作是数据的添加、删除
add / addAll
remove / removeAll / removeIf
借助 Iterator 接口,Collection 还具备了数据的循环
public interface Collection<E> extends Iterable<E>{
//...
// 对数据循环
Iterator<E> iterator();
}
通过 Iterable 接口, 标准库中的集合都可以使用 forEach 循环。
具体的实现类
集合类型 描述
ArrayList 一种可以动态增长和缩减的索引序列
LinkedList 一种可以在任何位置进行高效地插人和删除操作的有序序列
ArrayDeque 一种用循环数组实现的双端队列
HashSet 一种没有重复元素的无序集合
TreeSet 一种有序集
EnumSet 一种包含枚举类型值的集
LinkedHashSet 一种可以记住元素插人次序的集
PriorityQueue 一种允许高效删除最小元素的集合
HashMap 一种存储键/ 值关联的数据结构
TreeMap 一种键值有序排列的映射表
EnumMap 一种键值属于枚举类型的映射表
LinkedHashMap 一种可以记住腱/ 值项添加次序的映射表
WeakHashMap 一种其值无用武之地后可以被垃圾回收器回收的映射表
IdentityHashMap 一种用 == 而不是用equals 比较键值的映射表
Collection
image-20200212145950872.png
Map
image-20200212150240334.png
虽然类很多 ,但是我们在教授中只需要交几个类就可以了,分别是下面:
ArrayList
LinkedList
HashSet
HashMap
TreeMap
然后加上工具类2个:
Collections
Arrays
List
有序集合,可以精确控制列表中每个元素的插入位置。通过整数索引获取列表中的元素。List允许出现重复的值 , 并可以精确控制列表中每个元素的插入位置,通过整数索引获取列表中的元素。
方法名 说明
add(E e) 增加单个数据
addAll(Collection<? extends E> c) 将一个 Collection 集合的数据添加到现在的集合中
remove(Object o) 删除指定的元素
contains(Object o) 判断集合是否包含指定的元素
size() 得到集合的数据总数
isEmpty() 判断集合是否有数据
get(int index) 通过索引获取对应的数据元素
set(int index, E element) 通过索引和新的元素替换原有内容
clear() 清空数据
toArray() 将List转为对象数组
ArrayList
ArrayList 是List 接口的大小可变数组的实现。实现了所有可选列表操作, 并允许包括null 在内的所有元素。除了实现List 接口外, 此类还提供一些方法来操作内部用来存储列表的数组的大小( 此类大致上等同于 vector 类, 但 vector 是同步的) 。
ArrayList 的底层是使用数组实现的,看下面的图
image-20200212171434134.png
可以看到,数组中的每一个元素,都存储在内存单元中,并且元素之间紧密排列,既不能打乱元素的存储顺序,也不能跳过某个存储单元进行存储。
ArrayList 底层既然是使用数组实现,那么特点就和数组一致:查询速度快,增删速度慢。
每个ArrayList 实例都有一个容量。该容量是指用来存储列表元素的数组的大小, 它总是至少等于列表的大小。随着向Array L ist 中小断添加元素, 其容量也自动增长。并未指定增长策略的细节,因为这不只是添加元素会带来分摊固定时间开销那样简单。我们可以使用默认构造函数创建容量为 10 的列表, 也可以初始化指定容量大小。
ArrayList 指定初始容量大小的构造器方法
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
}
}
常用的操作
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
// 添加数据
list.add(123);
list.add(346);
// 替换数据
list.set(1, 777);
// 将list中的所有数据放到 list2 中
List<Integer> list2 = new ArrayList<>();
list2.addAll( list );
// 循环list2中所有的数据
for (Integer integer : list2) {
System.out.println( integer );
// 删除循环出的对象
list2.remove(integer);
}
// list 集合是否有数据
if( !list.isEmpty() ) {
System.out.println("list.size = "+ list.size());
// 清空 list
list.clear();
}
// 在清空list后,再看list现在有多少数据
System.out.println("list.size = "+ list.size());
}
}
LinkedList
LinkedList 是一个链表结构,可当作堆栈、队列、双端队列 。链表是一种在物理上非连续、非顺序的数据结构,由若干节点(node)所组成。
单链表
image-20200212173413313.png
单向链表的每一个节点包含两部分,一部分是存放数据的变量data,另一部分是指向下一个节点的指针next。正如地下党的联络方式,一级一级,单线传递:
private static class Node {
int data;
Node next;
}
双链表
双向链表比单向链表稍微复杂一些,它的每一个节点除了拥有data和next指针,还拥有指向前置节点的prev指针
image-20200212173517719.png
和数组不同,链表存储数据的时候,则采用了见缝插针的方式,链表的每一个节点分布在内存的不同位置,依靠next指针关联起来。这样可以灵活有效地利用零散的碎片空间。
image-20200212173548104.png
LinkedList 做数据的添加和删除操作时速度很快,但是做查询的时候速度就很慢,这和 ArrayList 刚好相反。
适用 LinkedList 独有的方法,在集合前后做数据插入
public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
list.add(123);
// 添加相同的数据
list.add(123);
list.add(456);
list.add(789);
list.add(101);
// 将数据 111 添加到 list 的末尾
list.addLast(111);
// 将数据 999 添加到 list的最前面
list.addFirst(999);
for (int i = 0; i < list.size(); i++) {
System.out.println( list.get(i) );
}
}
注意代码中声明和实现都是 LinkedList , 如果声明的是 List 接口 , addFirst 和 addLast 方法是不可见的
Set
Set 的特点是去重,如果相同的数据只会保留一个。集合内的数据是一个无序的状态,。当然也有例外,TreeSet就是有序的。
方法名 说明
add(E e) 增加单个数据
addAll(Collection<? extends E> c) 将一个 Collection 集合的数据添加到现在的集合中
remove(Object o) 删除指定的元素
contains(Object o) 判断集合是否包含指定的元素
size() 得到集合的数据总数
isEmpty() 判断集合是否有数据
clear() 清空数据
toArray() 将List转为对象数组
可以看到,Set 和 List 中的方法都是相同的,作用也一致。但是 Set 是没有直接获取数据的方法。我们更多的时候使用的是 List 和 Map。
Has