第9章 集合

简介: 集合体系、集合的数据结构以及操作。

9.1 概念

  • 用于存储数量不等的多个对象、具有映射关系的关联数组。

9.2 集合框架体系

  1. 单列集合(Collection):存放单个对象,有两个重要的子接口,其实现子类都是单列集合。
  • List:
  • ArrayList
  • Vector
  • LinkedList
  • Set:
  • HashSet
  • LinkedHashSet
  • TreeSet
  1. 双列集合(Map):存放k-v形式的数据
  • Hashtable
  • Properties
  • HashMap
  • LinkedHashMap
  • TreeMap

9.3 Collection

9.3.1 Collection接口的特点

  1. Collection接口没有直接的实现子类,是通过它的子接口Set和List实现的。
  2. Collection实现子类可以存放多个元素,每个元素可以是Object
  3. Collection的实现类,List是有序的,Set不是有序的
  4. Collection的实现类,有的可以存放重复的元素,有的不可以

9.3.2 Collection接口实现类遍历元素方式

  1. Tterator(迭代器):
  • 所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个迭代器
  • Iterator仅用于集合遍历,本身并不存放对象。
  • 使用流程:
  • 创建迭代器:Iterator it = 集合对象.iterator()
  • 遍历对象:while(it.hasNext()){it.next()}
  • 使用it.next()之前必须使用hasNext()方法进行判断,否则在调用完最后一条时会抛出NoSuchElementException异常。
  • while循环结束后,迭代器指向集合中最后一个元素,如果再调用next()方法,会抛出异常,如果希望再次遍历,则需要重置迭代器:it = 集合对象.iterator()
  • IDEA快捷模板:ititCtrl+j显示所有的快捷模板
  1. 增强for循环:
  • 语法:for( 元素类型 元素名 : 集合名或数组名){ 访问元素 }
  • 只能用于遍历集合和数组
  • 本质就是简化版的迭代器(底层代码)。
  1. 不可用普通for循环,因为子接口Set不支持索引

9.4 List

9.4.1 特点

  1. List集合类的元素有序(添加和取出顺序一致),且可重复。
  2. List集合类每个元素都有器对应的顺序索引,可通过索引取出元素——使用get()
  3. List接口实现类常用的有ArrayListLinkedListVector

9.4.2 常用方法

变量和类型

方法

描述

void

add(int index, E element)

将指定元素插入此列表中的指定位置(可选操作)。

boolean

add(E e)

将指定的元素追加到此列表的末尾(可选操作)。

boolean

addAll(int index, Collection<? extends E> c)

将指定集合中的所有元素插入到指定位置的此列表中(可选操作)。

boolean

addAll(Collection<? extends E> c)

将指定集合中的所有元素按指定集合的迭代器(可选操作)返回的顺序追加到此列表的末尾。

void

clear()

从此列表中删除所有元素(可选操作)。

boolean

contains(Object o)

如果此列表包含指定的元素,则返回 true

boolean

containsAll(Collection<?> c)

如果此列表包含指定集合的所有元素,则返回 true

static <E> List<E>

copyOf(Collection<? extends E> coll)

以迭代顺序返回包含给定Collection的元素的 unmodifiable List

boolean

equals(Object o)

将指定对象与此列表进行比较以获得相等性。

E

get(int index)

返回此列表中指定位置的元素。

int

indexOf(Object o)

返回此列表中第一次出现的指定元素的索引,如果此列表不包含该元素,则返回-1。

boolean

isEmpty()

如果此列表不包含任何元素,则返回 true

Iterator<E>

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()

返回此列表中的元素数。

default void

sort(Comparator<? super E> c)

根据指定的Comparator引发的顺序对此列表进行排序。

List<E>

subList(int fromIndex, int toIndex)

返回指定的 fromIndex (包含)和 toIndex (不包括)之间的此列表部分的视图。

Object[]

toArray()

以适当的顺序(从第一个元素到最后一个元素)返回包含此列表中所有元素的数组。

<T> T[]

toArray(T[] a)

以适当的顺序返回包含此列表中所有元素的数组(从第一个元素到最后一个元素);返回数组的运行时类型是指定数组的运行时类型。

9.4.3 遍历方式

  1. 使用Iterator迭代器:迭代器的next()取出的就是每个对象
  2. 使用增强for循环
  3. 使用普通for循环

9.4.4 ArrayList

  1. 特点:
  • 可以存放null,且可存放多个
  • 基本等同于Vector,但ArrayList线程不安全。
  1. 底层:
  • ArrayList的数据由一个transient修饰的Object[] 存储。可变数组
  • transient表示不可被序列化
  • ArrayList():初始容量为0,第一次添加元素容量变为10,后续扩容按照当前容量的1.5倍增大
  • ArrayList(int initialCapacity):初始容量为initialCapacity,后续扩容按照当前容量的1.5倍增大
  1. IDEA补充知识:
  • IDEA在debug时,会阉割数据
  • 解决方式:(去掉下列两个红框的勾选)。

9.4.5 Vector

  1. 特点:
  • 可以存放null,且可存放多个
  • Vector类的操作方法带有synchronized,所以是线程安全的。
  1. 底层:
  • 底层使用Object[]存放数据
  • Vector():初始容量0,第一次添加后10,不足时按当前容量的2倍扩容。
  • Vector(int initialCapacity):初始容量为initialCapacity,不足时按当前容量的2倍扩容。

9.4.6 LinkedList

  1. 特点:
  • 可以存放null,且可存放多个
  • 线程不安全,添加删除效率比数组高,查找效率较低
  1. 底层:
  • 底层维护了一个双向链表
  • LinkedeList中有两个属性first、last,分别指向链表中第一个和最后一个节点
  • 每个节点都含有3个属性:prev、next、item
  • 只有无参构造器LinkedList(),默认容量0,添加一个数组,容量+1。
  1. LinkedList和ArrayList的选择
  • 改查较多,选择ArrayList
  • 增删较多,选择LinkedList
  • 业务中,一般查询较多、增删较少,建议使用ArrayList

9.5 Set

9.5.1 特点

  1. 无序(添加和取出顺序不一定一致),元素不可重复,最多只可包含一个null。
  • 元素不可重复按照equals是否相等理解。(基本数据类型会自动装箱,但包装类重写了equals方法,比较的还是具体值;字面量字符串在常量池;构造器创建的字符串比较的也是具体内容(equals))
  • 但一旦数据确定,每次的取出顺序一致
  • hashcode是根据对象的地址值确定的
  1. 没有索引
  2. 常用实现类有:HashSetLinkedHashSetTreeSet

9.5.2 常用方法

变量和类型

方法

描述

boolean

add(E e)

如果指定的元素尚不存在,则将其添加到此集合(可选操作)。

boolean

addAll(Collection<? extends E> c)

如果指定集合中的所有元素尚未存在(可选操作),则将其添加到此集合中。

void

clear()

从该集合中删除所有元素(可选操作)。

boolean

contains(Object o)

如果此set包含指定的元素,则返回 true

boolean

containsAll(Collection<?> c)

如果此集合包含指定集合的所有元素,则返回 true

static <E> Set<E>

copyOf(Collection<? extends E> coll)

返回包含给定Collection的元素的 unmodifiable Set

boolean

equals(Object o)

将指定对象与此set进行相等性比较。

boolean

isEmpty()

如果此集合不包含任何元素,则返回 true

Iterator<E>

iterator()

返回此set中元素的迭代器。

boolean

remove(Object o)

如果存在,则从该集合中移除指定的元素(可选操作)。

boolean

removeAll(Collection<?> c)

从此集合中删除指定集合中包含的所有元素(可选操作)。

boolean

retainAll(Collection<?> c)

仅保留此集合中包含在指定集合中的元素(可选操作)。

int

size()

返回此集合中的元素数(基数)。

default Spliterator<E>

spliterator()

在此集合中的元素上创建 Spliterator

Object[]

toArray()

返回包含此set中所有元素的数组。

<T> T[]

toArray(T[] a)

返回一个包含此set中所有元素的数组; 返回数组的运行时类型是指定数组的运行时类型。

9.5.3 遍历方式

  1. 迭代器
  2. 增强for循环
  3. 不能使用普通for循环

9.5.4 HashSet

  1. 特点:
  • 可以存放null,但只能存放一个。
  • 相同数据只能存放一个
  • 注意字符串的特殊性(面试题)
  • 其他对象可以添加成功:
  • 数据存入与取出顺序不一定一致
  1. 底层(数据结构):
  • 底层实际上是HashMap,而HashMap的底层是数组+链表+红黑树
  • 调用HashSet构造器后,会先执行HashMap()构造器,并创建table表(数组),初始容量为0,再取得加载因子0.75
  • 添加元素时,先计算得到该元素的hash值,并转换得到索引值
  • 第一次添加时,将table扩容到16,并把第一次添加的元素放到表中的指定位置
  • 后续添加时,比较索引值,如果表中该索引已有值,则在该值后的数据链中比较,添加到最后
  • 注意:这里比较元素内容是否相同,取决于加入元素的equals()方法,通过重写equals()hasCode()可以控制元素能否加入
  • JAVA8中,如果数据链的数据已有8个,先对表按2倍进行扩容,如果表已经扩容过,并且表到了64,则将该表转换为红黑树
  • 平时链表数据没到8个,但表到达12个(16*0.75),就会对表按照2倍进行扩容,并以此类推
  • 数组类型为HashMap$Node,数据类型为HashMap$Node

9.5.5 LinkedHashSet

  1. 特点:
  • HashSet的子类、并实现了Set接口
  • 可以存放null、但只能存放一个
  • 相同数据只能添加一个
  • 元素取出顺序固定,且与加入顺序一致。
  1. 底层:
  • 底层使用了一个hashtable(数组) 和双向链表存储数据,
  • 数组是HashMap$Node类型
  • 数组有head和tail
  • 数据类型为LinkedHashMap$Entry类型,LinkedHashMap是HashMap的子类。
  • 每个元素有before和after
  • 初始容量为0,第一次添加时,直接将数组table 扩容到16,再添加一个元素时,先求hash值,再求索引,确定在表中的位置,然后再将元素添加到链表中【机制同HashSet】

9.5.6 TreeSet

  1. 特点:
  • 使用无参构造器时,仍然是无序的(输出顺序与输入顺序不一致)。
  • 使用带比较器的构造器TreeSet(Comparator<? super E> comparator):可以设定指定的添加规则及顺序。
  • 添加规则:取决于比较器中比较的属性,比较器返回0时则不能加入
  • 顺序:根据比较器指定的顺序或者逆序确定。
  • 匿名内部类的比较器规则会被创建时底层的compare()调用
  1. 底层:
  • 底层就是TreeMap,TreeMap底层就是Entry
  • 存放的数据类型是TreeMap$Entry
  • 初始化大小是0,添加一个容量+1

9.6 Map

9.6.1 Map接口的特点

  1. Map与Collection并列存在,用于保存具有映射关系的数据。key-value(双列元素)。
  2. Map中的key和value可以时任意数类型,会封装到HashMap$Node对象中,数据类型为HashMap$Node
  3. Map中的key不允许重复,原因同HashSet(key相同则hashcode和索引值相同,在table表(数组)的位置相同),相同的key等价于替换元素。
  4. Map中的value可以重复
  5. Map中的key可以为null,value也可以为null,key为null时只能添加一个(等价于替换),value可以多个为null
  6. Map中常用String作为key,实际上key只要是Object就都可以(包含基本数据类型)
  7. Map中的key和value存在一一对应关系,使用get(key),就可以得到唯一的一个value
  8. Map中的数据存储:一对k-v是存放在HashMap$Node中的,由于Node实现了Entry接口,所以在一定理解上,可以说一对k-v就是一个Entry
  • 源码韩顺平分析:
  • k-v 最后是 HashMap$Node node = newNode(hash, key, value, null)
  • k-v 为了方便程序员的遍历,还会创建 EntrySet 集合 ,该集合存放的元素的类型 Entry, 而一个Entry对象就有k,v EntrySet<Entry<K,V>> 即: transient Set<Map.Entry<K,V>> entrySet;
  • entrySet 中, 定义的类型是 Map.Entry ,但是实际上存放的还是 HashMap$Node这是因为 static class Node<K,V> implements Map.Entry<K,V>
  • 当把 HashMap$Node 对象 存放到 entrySet 就方便我们的遍历, 因为 Map.Entry 提供了重要方法K getKey(); V getValue();
  • 源码个人理解:
  • k-v在创建时执行的是HashMap$Node node = newNode(hash, key, value, null),所以k-v的数据类型就是HashMap$Node,数组类型也是HashMap$Node
  • Map.Entry有重要的getKey()getValue()可以获取元素的k-v值,有了这两个方法,就大大方便了使用(遍历)。
  • 而在Map中,通过keySet()获取Set类型的所有key,values()获取Collection类型的所有vaule,将这二者存放在了一个Set表中。
  • 这个Set表就是EntrySet集合,集合中每一个元素是Entry类型,拥有key和valuetransient Set<Map.Entry<K,V>> entrySet;
  • EntrySet和Node都是HashMap的内部类
  • 为了使HashMap$Node类型的数据能够用上面两个方法,HashMap$Node类实现了Map.Entry:static class Node<K,V> implements Map.Entry<K,V>
  1. 常用实现类:HashMap(使用率最高)HashtableProperties

9.6.2 Map接口常用方法

变量和类型

方法

描述

void

clear()

从此映射中删除所有映射(可选操作)。

boolean

containsKey(Object key)

如果此映射包含指定键的映射,则返回 true

Set<Map.Entry<K,V>>

entrySet()

返回此映射中包含的映射的Set视图。

V

get(Object key)

返回指定键映射到的值,如果此映射不包含键的映射,则返回 null

boolean

isEmpty()

如果此映射不包含键 - 值映射,则返回 true

Set<K>

keySet()

返回此映射中包含的键的Set视图。

V

put(K key, V value)

将指定的值与此映射中的指定键相关联(可选操作)。

V

remove(Object key)

如果存在,则从该映射中移除键的映射(可选操作)。

int

size()

返回此映射中键 - 值映射的数量。

Collection<V>

values()

返回此映射中包含的值的Collection视图。

9.6.3 Map接口遍历方法

  1. 方式一:使用Map的keySet()方法取出所有的key,而取出的key类型为Set,因此可以使用两种方式:
  • 增强for循环:
  • 使用迭代器:
  • 迭代器的next()取出的是key
  1. 方式二:使用Map的values()方法取出所有的value,而取出的value类型为Collection,因此可以使用两种方式:
  • 增强for循环:
  • 使用迭代器:
  • 迭代器的next()取出的是value
  1. 方式三:使用Map的entrySet()方法取出所有的entry,而取出的entry类型为HashMap$Node,HashMap$Node实现了Map.Entry,而Map.Entry有getKey()getValue()
  • 使用EntrySet的增强for循环:
  • 前提条件:使用向上转型,转为Map.Entry。
  • 使用迭代器:
  • 迭代器的next()取出的是entry,entry类型为HashMap$Node,HashMap$Node实现了Map.Entry。

9.6.4 HashMap

  1. 特点:
  • 使用率最高
  • key不能重复,但value可以重复,允许使用null的key和null的value
  • 如果添加相同的key,则会覆盖原来的key-val,等同于修改(替换)
  • 线程不安全
  1. 底层:
  • 底层以key-val的方式来存储数据(数组及数据均为HashMap$Node类型)
  • 与HashSet一样,不保证映射顺序,因为底层是以hash表的方式来存储(数组+链表+红黑树)
  • 扩容机制:同HashSet,唯一区别在于HashSet计算哈希值是元素,HashMap计算时是key
  • 初始化数据容量为0

9.6.5 LinkedHashMap

  1. 特点:
  • 继承了HashMap,实现了Map。
  • 线程不安全
  • 如果有相同key,后者替换前者;value可以重复,允许使用null的key和null的value
  1. 底层
  • 底层使用数组+双向链表 转 红黑树
  • 初始容量为0

9.6.6 Hashtable

  1. 特点:
  • 键和值都不能为null,会抛出NullPointerException
  • 线程安全的
  1. 底层:
  • 底层是一个数组,类型为Hashtable$Entry,实现了Map.Entry。元素类型也为Hashtable$Entry
  • 初始化大小为11,临界值为8(11*0.75)
  • 当添加数据数量到达8时,对数组进行扩容,扩容为当前容量 * 2 + 1,新的临界值变为当前容量 * 0.75

9.6.7 Properties

  1. 特点:
  • 继承了Hashtable:不能添加null的key或value,会发生NullPointerException
  • 线程安全
  • 如果有相同key,后者替换前者
  • 可以用与xxx.properties文件中,加载数据到Properties类中进行读取和修改
  1. 底层
  • 初始容量为0,底层使用数组,元素类型、扩容机制同Hashtab

9.6.8 TreeMap

  1. 特点:
  • 使用无参构造器时,仍然是无序的(输出顺序与输入顺序不一致)。
  • 使用带比较器的构造器TreeMap(Comparator<? super E> comparator):可以设定指定的添加规则及顺序。
  • 添加规则:取决于比较器中比较的属性,比较器返回0时则不能加入
  • 顺序:根据比较器指定的顺序或者逆序确定。
  • 匿名内部类的比较器规则会被创建时内部的compare()调用
  1. 底层:
  • 底层就是Entry
  • 存放的数据类型是TreeMap$Entry
  • 初始化大小是0,添加一个容量+1

比较(个人总结):

注意:容量和size()不同。size()指的是有多少实际的数据。

9.7 Collections工具类

9.7.1 介绍

  1. 操作Set、List、Map等集合的工具类。
  2. 能够对集合进行排序、查找、修改等工作。

https://www.runoob.com/manual/jdk1.6/java.base/java/util/Collections.html

9.7.2 排序操作:

  1. reverse(List):反转List中的元素顺序
  2. shuffle(List):对List集合进行随机排序,每次调用都进行一次随机
  3. sort(List):对List结合的元素进行自然排序(从小到大)
  4. sort(List, Comparator):根据比较器的规则对List集合的元素进行排序
  5. swap(List, int i, int j):交换List和i和j位置的元素
  • i或j超范围时抛出索引越界异常

变量和类型

方法

描述

static void

reverse(List<?> list)

反转指定列表中元素的顺序。

static void

shuffle(List<?> list)

使用默认的随机源随机置换指定的列表。

static <T extends Comparable<? super T>> void

sort(List<T> list)

根据其元素的natural ordering ,将指定列表按升序排序。

static <T> void

sort(List<T> list, Comparator<? super T> c)

根据指定比较器引发的顺序对指定列表进行排序。

static void

swap(List<?> list, int i, int j)

交换指定列表中指定位置的元素。

9.7.3 查找、替换

变量和类型

方法

描述

static <T extends Object & Comparable<? super T>> T

max(Collection<? extends T> coll)

根据元素的 自然顺序返回给定集合的最大元素。

static <T> T

max(Collection<? extends T> coll, Comparator<? super T> comp)

根据指定比较器引发的顺序返回给定集合的最大元素。

static <T extends Object & Comparable<? super T>> T

min(Collection<? extends T> coll)

根据元素的 自然顺序返回给定集合的最小元素。

static <T> T

min(Collection<? extends T> coll, Comparator<? super T> comp)

根据指定比较器引发的顺序返回给定集合的最小元素。

static int

frequency(Collection<?> c, Object o)

返回指定集合中等于指定对象的元素数。

static <T> boolean

replaceAll(List<T> list, T oldVal, T newVal)

用列表替换列表中所有出现的指定值。

static <T> void

copy(List<? super T> dest, List<? extends T> src)

将一个列表中的所有元素复制到另一个列表中。

  • 使用copy()方法前必须确认目标集合与源集合有同样的size(注意size不是容量),否则会发生数组越界异常。
目录
相关文章
|
1月前
|
存储 Java 索引
JAVASet集合
JAVASet集合
24 0
|
4月前
集合
数组的长度不可以发生改变。 但是ArrayList集合的长度是可以随意变化的。 对于ArrayList来说,有一个尖括号代表泛型。 泛型:也就是装在集合当中的所有元素,全都是统一的什么类型。 注意:泛型只能是引用类型,不能是基本类型。 注意事项: 对于ArrayList集合来说,直接打印得到的不是地址值,而是内容。如果内 容是空,得到的是空的中括号:[]
27 0
|
6月前
|
存储 Java 索引
1.9 集合
1.9 集合
27 1
|
10月前
|
索引
集合理解
集合的个人理解笔记 与二叉查找树规律
49 0
|
11月前
16 集合(下)
16 集合(下)
72 0
|
11月前
|
存储
|
存储 算法 安全
集合总结
集合总结
73 0
GoogleGuava - 第 2 章 集合——不可变集合
GoogleGuava - 第 2 章 集合——不可变集合
99 0
GoogleGuava - 第 2 章 集合——不可变集合
CAN知识集合
CAN知识集合
154 0
CAN知识集合