目录
集合VS数组
数组
- 长度开始时必须指定,而且一旦指定,不能修改
- 保存的必须为同一类型的元素
- 使用数组进行增加/删除元素比较麻烦
集合
- 可以动态保存任意多个对象,使用比较方便
- 提供一系列方便的操作对象的方法
- 使用集合进行增加/删除元素比较简洁
框架体系
Collection接口
基本介绍
- Collection实现子类可以存放多个元素,每个元素可以是Object
- 有些Collection的实现类,可以存放重复的元素,有些不可以
- 有些Collection的实现类,有些是有序的,有些不是有序的
- Collection接口没有直接的实现子类,是通过它的子接口List和Set来实现的
迭代器遍历
- Iterator对象称为迭代器,主要用于遍历Collection集合中的元素
- 所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象,即可以返回一个迭代器
- Iterator仅用于遍历集合,Iterator本身并不存放对象
执行原理:
- .hasNext(); 判断是否还有下一个元素
- .next(); 作用:
1⃣️指针下移
2⃣️将下移以后集合位置上的元素返回 - 在调用iterator.next(); 方法之前必须调用iterator.hasNext(); 进行检测。若不调用,且下一条记录无效,直接调用iterator.next(); 会抛出
for循环加强
- 基本语法
for(元素类型 元素名 : 集合名/数组名){
访问元素
}
代码示例
package Java.Collection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
/**
* @author 鹿青舟
* @version 1.0
*/
public class CollectionIterator {
@SuppressWarnings({"all"})
public static void main(String[] args) {
Collection col = new ArrayList();
col.add(new book("西游记", "吴承恩", 13.2));
col.add(new book("三国演义", "罗贯中", 22.2));
col.add(new book("红楼梦", "曹雪芹", 33.2));
Iterator iterator = col.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
System.out.println(obj);
}
for (Object book : col) {
System.out.println(book);
}
}
}
class book{
private String name;
private String author;
private double price;
public book(String name, String author, double price) {
this.name = name;
this.author = author;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "book{" +
"name='" + name + '\'' +
", author='" + author + '\'' +
", price=" + price +
'}';
}
}
List接口
基本介绍
- List接口是Collection接口的子接口
- List集合类中元素=有序(即添加顺序和取出顺序一致)、且可重复
- List集合中的每个元素都有对应的顺序索引,即支持索引
- List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素
常用方法
- 1⃣️在索引1插入对象
list.add(1,"hi");
- 2⃣️在索引1插入集合2
list.addAll(1,list2);
- 3⃣️获取索引3的元素
list.get(3);
- 4⃣️返回"tom"对象第一次出现的索引
list.indexOf("tom");
- 5⃣️返回"tom"对象最后一次出现的索引
list.lastindexOf("tom");
- 6⃣️替换索引1对象为"tom"
list.set(1,"tom");
- 7⃣️移除索引0对象
list.remove(0);
遍历方式
- 使用iterator
Iterator iterator = col.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
System.out.println(obj);
}
- 使用增强for
for (Object book : col) {
System.out.println(book);
}
- 使用普通for
for (int i = 0; i < col.size(); i++) {
Object obj = col.get(i);
System.out.println(obj);
}
ArrayList
注意示例
- ArrayList可以加入多个null
- ArrayList是由数组来实现数据存储的
- ArrayList基本等同于Vector
- ArrayList是线程不安全的,没有synchronized,多线程不建议使用
底层源码
- 1⃣️创建数组
无参构造:
有参构造:
- 2⃣️
1)确定是否扩容
2)执行、赋值
- 3⃣️
1)判断是否为空,是则扩容10
2)确定大小是否够
- 4⃣️
1)记录被修改次数
2)判断空间是否够,不够则去扩容
- 5⃣️
1)按1.5倍扩容
2)如果第一次则扩容10
3)用copyOf()扩容
Vector
注意示例
- Vector底层也是一个对象数组
- Vector是线程安全的,即线程同步,有synchronized,多线程建议使用
比较
分类 | 底层结构 | 版本 | 线程安全/效率 | 扩容倍数 |
---|---|---|---|---|
ArrayList | 可变数组 | jdk1.2 | 不安全,效率高 | 默认空间10,1.5倍扩容 |
Vector | 可变数组 | jdk1.0 | 安全,效率不高 | 默认空间10,2.0倍扩容 |
LinkedList
注意示例
- LinkedList实现了双向链表、双端队列的特点
- LinkedList是线程不安全的,即没有实现同步
- LinkedList底层维护了一个双向链表,还维护了两个属性first和last分别指向首节点和尾节点,每个节点(Node)里面还维护了prev、next、item三个属性
- LinkedList元素的添加和删除,不是通过数组完成的,效率高
比较
分类 | 底层结构 | 增删效率 | 查改效率 |
---|---|---|---|
ArrayList | 可变数组 | 较低,数组扩容 | 较高 |
LinkedList | 可变数组 | 较高,链表追加 | 较低 |
Set接口
基本介绍
- 元素无序(即添加顺序、取出顺序不一致)、不允许重复
- 没有索引,不能使用索引方式来获取元素
遍历方式
- 使用iterator
Iterator iterator = col.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
System.out.println(obj);
}
- 使用增强for
for (Object book : col) {
System.out.println(book);
}
不能使用普通for循环,不能使用索引
HashSet⚠️‼️‼️⚠️
注意示例
- HashSet实现了Set接口
- HashSet实际上是HashMap(HashMap底层:数组+链表+红黑树)
- 不保证元素有序,不能有重复的元素/对象
- 取出顺序一旦确定,就不会变化
底层源码
- 1⃣️add()
- 2⃣️put()
- 3⃣️putval()
- hash()
- resize()
LinkedHashSet
注意示例
- 是HashSet的子类,底层是一个LinkedHashMap,底层维护了一个数组+双向链表
- 不能添加重复元素,且是有序的
- LinkedHashSet中维护了一个hash表和双向链表,还维护了head和tail分别指向头节点和尾节点,每个节点有before和after属性,形成双向链表
- 插入顺序和遍历顺序一致,添加时先求哈希值,再求索引
Map接口
基本介绍
- Map与Collection并列存在,用于保存具有映射关系的数据(key- value双列元素)
- Map中的key和value可以是任何引用类型的数据,会封装到HashMap$Node对象中
- Map中的key不允许重复,当有相同的key,就等价于替换,value可以重复
- Map中的key、value也可以是null,key为null只能有一个,value为null可以有多个
- 一般常用String类作为Map中key
- key和value之间存在单向一对一关系,即通过key找到对应的value
注意示例
- key-value最后是HashMap$Node
node = newNode(hash,key,value,null);
- key-value为了方便程序员的遍历,创建了EntrySet集合,存放的元素类型Entry,Entry对象有key-value
- Map.EntrySet中定义的类型是Map.Entry,但实际上存放的是HashMap$Node
static class Node<K,V> implements Map.Entry<K,V>
- 当把HashMap$Node对象存放到EntrySet方便遍历,因为Map.Entry提供了getKey和getValue
for(Object obj : map.entrySet()){
Map.Entry entry = (Map.Entry) obj;
}
Map体系
常用方法
- 添加
map.put("No1",1);
- 根据键删除映射关系
map.remove("No1");
- 根据键获取值
map.get("No1");
- 获取元素个数
map.size();
- 判断个数是否为0
map.isEmpty();
- 清除key-value
map.clear();
- 查看键是否存在
map.containskey("No1");
遍历方式
- 根据KeySet
//1⃣️
System.out.println("------1------");
for (Object key : map.keySet()) {
System.out.println(key + "-" + map.get(key));
}
System.out.println("------1-2------");
Iterator iteratorKey = map.keySet().iterator();
while (iteratorKey.hasNext()) {
Object next = iteratorKey.next();
System.out.println(next + "-" + map.get(next));
}
- 根据Values
//2⃣️
System.out.println("------2------");
for (Object value : map.values()) {
System.out.println(value);
}
System.out.println("------2-2------");
Iterator iteratorVal = map.values().iterator();
while (iteratorVal.hasNext()) {
Object next = iteratorVal.next();
System.out.println(next);
}
- 根据entrySet
//3⃣️
System.out.println("------3------");
for (Object enterSet : map.entrySet()) {
Map.Entry entry = (Map.Entry) enterSet;
System.out.println(entry);
}
System.out.println("------3-2------");
Iterator iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
// System.out.println(next.getClass());
}
HashMap
注意示例
- Map接口的常用实现类:HashMap(使用最多)、Hashtable、Properties
- HashMap是线程不安全的,即线程不同步,方法没有做同步互斥的操作,没有synchronized
- jdk7的HashMap底层:数组+链表
- jdk8的HashMap底层:数组+链表+红黑树
扩容机制
- 第一次添加,扩容table容量为16,临界值(threshold)为12,加载因子(loadfactor)为0.75
- 再Java8中,如果一条链表的元素个数为超过TREEIFY_THRESHOLD(默认8),并且table大小 >= MIN_TREEIFY_CAPACITY(默认64)就会进行树化
- 元素个数到8,table是16,继续向链表添加,table扩容——不树化
- 元素个数到9,table是32,继续向链表添加,table扩容——不树化
- 元素个数到10,table是64,继续添加——树化
底层源码
- 执行构造器new HashMap();初始化加载因子 loadfactor = 0.75
HashMap$Node[] table = null;
- 执行put,分别调用hash.putVal方法
public V put(K key , V value){
return putVal(hash(key), key, value, false, true)
}
- 执行putVal,若空间不足,执行resize
Hashtable
注意示例
- Hashtable的键和值都不能为null,否则会抛出NullPointerException
- Hashtable是线程安全的
扩容机制
- 底层有数组Hashtable$Entry[] ;初始化大小为11,加载因子0.75
- 将key-value封装到Entry
addEntry(hash,key,value,index)
- 按(*2倍 + 1)扩容
int newCapacity = (oldCapacity << 1) + 1;
比较
分类 | 版本 | 线程安全 | 效率 | 键值对 |
---|---|---|---|---|
HashMap | jdk1.2 | 不安全 | 效率高 | 允许null键值 |
Hashtable | jdk1.0 | 安全 | 效率较低 | 不允许null键值 |