java学习笔记(十四) - 集合
java学习笔记(十四) - 集合
执笔一、概念
- 可以动态保存任意多个对象,使用比较方便
- 提供了一系列方便的操作对象方法:add、remove、set、get
- 使用集合添加、删除新元素代码更加简洁
二、集合框架体系
1. 单列集合
1.1 使用
1 | //单列集合 collection |
1.2 Collection接口实现类的特点
1 | public interface Collection<E> extends Iterable<E> |
- collection实现子类可以存放多个元素,每个元素可以是Object
- 有些Collection的实现类,可以存放重复的元素,有些不可以
- 有些Collection的实现类,有些是有序的(List),有些不是有序(Set)
- Collection接口没有直接的实现子类,是通过它的子接口Set 和List来实现的
1.3 常用方法
1 | 1) add:添加单个元素 |
1 | public class CollectionMethods { |
1. 4 遍历方式
1.4.1 Iterator(迭代器)
Iterator对象称为迭代器,主要用于遍历Collection集合中的元素。
所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了lterator接口的对象,即可以返回一个迭代器。
Iterator仅用于遍历集合,Iterator 本身并不存放对象。
执行原理:
- ```java
Iterator iterator = collection.iterator();//得到一个集合的迭代器
//方法
hasNext();//判断是否还有下一个元素
while(iterator.hasNext()) {
}//next();//指针下移,将下移以后集合位置上的元素返回 System.out.println(iterator.next());
//在调用iterator.next()方法之前必须要调用iterator.hasNext()进行检测是否有下一条,如果下一条记录无效,直接调用iterator.next()会抛出NoSuchElementException异常1
2
3
4
5
6
7
8
9
10
11
12
##### 1.4.2 增强for
```java
//语法
for(元素类型 元素名 : 集合名或者数组名){
访问元素
}
//案例
for(Object object : col) {
System.out.println(object);
}
- ```java
2. 双列集合
1 | HashMap hashMap = new HashMap(); |
三、List接口
1 概念
- List 接口是 Collection 接口的子接口
- List集合类中元素有序(即添加顺序和取出顺序一致)、且可重复
- List集合中的每个元素都有其对应的顺序索引,即支持索引
- List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素
- JDK API中List接口的实现类有:ArrayList、LinkedList、Vector
1.1 常用方法
1 | List 集合里添加了一些根据索引来操作集合元素的方法 |
1 | public class ListExercise { |
2. 遍历的三种方式
1 | //方式一:使用iterator |
3. ArrayList
- permits all elements, including null ,ArrayList可以加入null,并且多个
- ArrayList是由数组来实现数据存储的门
- ArrayList 基本等同于Vector,除了ArrayList是线程不安全(执行效率高),在多线程情况下,不建议使用
1. 底层分析
ArrayList中维护了一个Object类型的数组elementData;transient Object[] elementData;
1
transient Object[] elementData; // non-private to simplify nested class access
当创建对象时,如果使用的是无参构造器,则初始elementData容量为0 (jdk7是10)
1
2
3
4private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}当添加元素时:先判断是否需要扩容,如果需要扩容,则调用grow方法,否则直接添加元素到合适位置
如果使用的是无参构造器,如果第一次添加,需要扩容的话,则扩容elementData为10,如果需要再次扩容的话,则扩容elementData为1.5倍。
- 如果使用的是指定容量capacity的构造器,则初始elementData容量为capacity
- 如果使用的是指定容量capacity的构造器,如果需要扩容,则直接扩容elementData为1.5倍。
4. Vector
4.1 概念
Vector底层也是一个对象数组
- ```java
protected Object[] elementData;1
2
3
4
5
6
7
8
9
10
- Vector是线程同步的,即线程安全
- ```java
public synchronized E get(int index) {
if (index >= elementCount) {
throw new ArraylndexOutOfBoundsException(index);
}
return elementData(index);
}
- ```java
4.2 比较
- Vector和ArrayList的比较
底层结构 | 版本 | 线程安全(同步)效率 | 扩容倍数 | |
---|---|---|---|---|
ArrayList | 可变数组 | jdk1.2 | 不安全,效率高 | 如果有参构造器1.5倍; 如果是无参构造器 :1、 第一次10;2、从第二次开始按1.5倍扩容 |
Vector | 可变数组Object[] | jdk1.0 | 安全,效率不高 | 如果是无参,默认为10,满后按2被进行扩容;如果指定大小,则每次按2倍进行扩容 |
5. LinkedList
- LinkedList底层实现了双向链表和双端队列特点
- 可以添加任意元素(元素可以重复),包括null
- 线程不安全
5.1 底层机制
- LinkedList底层维护了一个双向链表
- LinkedList中维护了两个属性first和last分别指向首节点和尾节点
- 每个节点(Node对象),里面又维护了prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个节点。最终实现双向链表
- 所以LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高。
1 | public class LinkedList01 { |
5.2 比较
- ArrayList和LinkedList的比较
底层结构 | 增删的效率 | 改查的效率 | |
---|---|---|---|
ArrayList | 可变数组 | 较低;数组扩容 | 较高 |
LinkedList | 双向链表 | 较高,通过链表追加 | 较低 |
5.3 选择
- 如何在ArrayList和LinkedList中选择
- 如果改查的操作多,选择ArrayList
- 如果增删的操作多,选择LinkedList
6. set接口
- 无序(添加和取出的顺序不一致),没有索引
不允许重复元素,最多包含一个null
可以使用迭代器和增强for进行遍历,不能使用索引方式进行遍历
6.1 HashSet
HashSet实现了Set接口
HashSet实际上是HashMap
- ```java
public HashSet() {
}map = new HashMap<>();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
- 不能有重复元素对象,可以存放null,但只有一个
- HashSet不保证元素是有序的,取决于hash后,再确定索引的结果
```java
public class HashSet01 {
public static void main(String[] args) {
HashSet set = new HashSet();
//1. 在执行add方法后会返回一个boolean值
//2. 如果添加成功,返回true,否则返回false
//3. 可以通过remove指定删除某个对象
System.out.println(set.add("john"));//T
System.out.println(set.add("lucy"));//T
System.out.println(set.add("john"));//F
System.out.println(set.add("jack"));//T
System.out.println(set.add("Rose"));//T
set.remove("john");
System.out.println("set=" + set);
//
set = new HashSet();//置空
System.out.println("set=" + set);//0
//4. HashSet 不能添加相同的元素/数据?
set.add("lucy");//添加成功
set.add("lucy");//添加失败
set.add(new Dog("tom"));//OK 不同的对象只是名字相同
set.add(new Dog("tom"));//OK
System.out.println("set=" + set);
//经典面试题
set.add(new String("TP"));//ok
set.add(new String("TP"));//添加失败
System.out.println("set=" + set);
}
}
//定义Dog类
class Dog {
private String name;
public Dog(String name) {
this.name = name;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
'}';
}
}
- ```java
6.1.1 底层机制
- HashSet底层是HashMap,HashMap底层是(数组+链表+红黑树)
1 | public class HashSetStructure { |
1 | public class HashSetExercise02 { |
6.2 LinkedHashSet
- LinkedHashedSet是HashSet的子类
- LinkedHashSet 底层是一个 LinkedHashMap,底层维护了一个数组+双向链表
- LinkedHashSet根据元素的hashCode值米决互元系时仔4直,问时使用链表维护元素的次序(图),这使得元素看起来是以插入顺序保存的
- LinkedHashSet 不允许添重复元素
1 | public class LinkedHashSetExercise { |
- 在LinkedHashSet中维护了一个hash表和双向链表(LinkedHashSet有head和 tail )
- 每一个节点有before和after属性,这样可以形成双向链表
- 在添加一个元素时,先求hash值,在求索引.,确定该元素在table的位置,然后将添加的元素加入到双向链表(如果已经存在,不添加[原则和hashset一样])tail.next = newElement;newElement.pre = tail;tail = newEelment;
- 这样的话,我们遍历LinkedHashSet也能确保插入顺序和遍历顺序一致
7. Map接口
7.1 实现类的特点(jdk8)
- Map与Collection并列存在。用于保存具有映射关系的数据:Key-Value
- Map 中的key和 value可以是任何引用类型的数据,会封装到HashMap$Node对象中
- Map 中的key 不允许重复,原因和HashSet一样(存放时会比较哈希值)
- Map中的value可以重复
- Map的key可以为null, value也可以为null,注意key 为null, 只能有一个,value为null ,可以多个
- 常用String类作为Map的key
7) key 和 value之间存在单向一对一关系,即通过指定的key 总能找到对应的value
7) 一组k-v是存放在一个Node结点中,因为Node实现了Entry接口
1 | public class Map_ { |
7.2 体系图
7.3 常用方法
1 | 1) put//添加 |
7.4 遍历方法
1 | 1) containsKey//查找键是否存在 |
1 | public class MapFor { |
7.5 HashMap
- Map接口的常用实现类:HashMap、Hashtable和Properties
- HashMap是 Map 接口使用频率最高的实现类
- HashMap 是以 key-val对的方式来存储数据(HashMap$Node类型)
- key 不能重复,但是值可以重复,允许使用null键和null值
- 如果添加相同的key,则会覆盖原来的key-val,等同于修改。(key不会替换,val会替换)
- 与HashSet一样,不保证映射的顺序,因为底层是以hash表的方式来存储的(jdk8的hashMap底层数组+链表+红黑树)
- HashMap没有实现同步,因此是线程不安全的,方法没有做同步互斥的操作,没有synchronized
7.5.1 底层机制
7.5.2 扩容机制
- HashMap底层维护了Node类型的数组table,默认为null
- 当创建对象时,将加载因子(loadfactor)初始化为0.75
- 当添加key-val时,通过key的哈希值得到在table的索引。然后判断该索引处是否有元素,如果没有元素直接添加。如果该索引处有元素,继续判断该元素的key是否和准备加入的key相等,如果相等,则直接替换val;如果不相等需要判断是树结构还是链表结构,做出相应处理。如果添加时发现容量不够,则需要扩容
- 第1次添加,则需要扩容table容量为16,临界值(threshold)为12
- 以后再扩容,则需要扩容table容量为原来的2倍,临界值为原来的2倍,即24,依次类推
- 在Java8中,如果一条链表的元素个数超过TREEIFY_THRESHOLD(默认是8),并且table的大小 >= MIN TREEIFY_CAPACITY(默认64),就会进行树化(红黑树)
7.6 HashTable
- 存放的元素是键值对:即K-V
- hashTable的键和值都不能为null
- hashTable使用方法基本上和HashMap一样
- hashTable是线程安全的,hashMap是线程不安全的
7.7 Hashtable和HashMap对比
版本 | 线程安全(同步) | 效率 | 允许null键null值 | |
---|---|---|---|---|
HashMap | 1.2 | 不安全 | 高 | 可以 |
Hashtable | 1.0 | 安全 | 较低 | 不可以 |
8. Properties
- 通常作为配置文件
- Properties类继承自Hashtable类并且实现了Map接口,也是使用一种键值对的形式来保存数据
- 使用特点和Hashtable类似
- Properties 还可以用于从 xxx.properties文件中,加载数据到Properties类对象,并进行读取和修改
1 | Properties properties = new Properties(); |
9. 小结
- 先判断存储的类型(一组对象[单列]或一组键值对[双列])
- 一组对象[单列]:Collection接口
- 允许重复:List
- 增删多:LinkedList[底层维护了一个双向链表]
- 改查多: ArrayList[底层维护 Object类型的可变数组]
- 不允许重复:Set
- 无序:HashSet[底层是HashMap,维护了一个哈希表即(数组+链表+红黑树)]
- 排序:TreeSet
- 插入和取出顺序一致:LinkedHashSet,维护数组+双向链表
- 一组键值对[双列]:Map
- 键无序:HashMap [底层是:哈希表 jdk7:数组+链表,jdk8:数组+链表+红黑树]键
- 排序:TreeMap
- 键插入和取出顺序一致: LinkedHashMap
- 读取文件Properties
三、Collection工具类
- Collections是一个操作 Set、List和Map等集合的工具类
- Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作
1. 排序操作(均为static方法)
1 | 1) reverse(List)//反转 List 中元素的顺序 |
1 | public class Collections_ { |
2. 查找、替换
1 | 1) Object max(Collection)//根据元素的自然顺序,返回给定集合中的最大元素 |
评论
匿名评论隐私政策
✅ 你无需删除空行,直接评论以获取最佳展示效果