集合的理解和好处
集合的框架体系图
集合主要分为两组:单列集合(Collection)和双列集合(Map)。单列指的是存放的是元素的值,双列指的是存放的元素是 key--value 形式的。
Collection 集合体系图
Map 集合体系图
Collection 接口及其常用方法
Collection 接口概述
Collection 接口(public interface Collection<E> extends Iterable<E>)实现类的特点:
- Collection 的实现子类可以存放多个元素,每个元素可以是 Object (或 Object 的子类)(每个元素都是对象,比如往集合里添加 10,底层也会包装成 new Integer(10); )
- 有些 Collection 的实现类可以存放重复元素,有些不可以
- 有些 Collection 的实现类是有序的(即存放和取出的顺序是一样的,如List),有些不是有序的(即存放和取出的顺序可能不一致,如 Set)
- Collection 接口没有直接的实现子类,而是通过 Collection 的子接口 Set 和List 来实现的
Collection 接口常用方法
Collection 遍历的两种方式
1、迭代器
Iterable 接口中有一个抽象方法,该方法返回一个 Iterator 对象,方法定义如下:
Iterator<T> iterator();
因为 Collection 实现了 Iterable 接口,所以实现了 Collection 接口的所有集合类都实现 iterator 这个抽象方法,即都可以返回一个 Iterator 对象
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
public class Test {
public static void main(String[] args) {
Collection c = new ArrayList<>();
c.add(10);
c.add(20);
c.add(30);
Iterator iterator = c.iterator();
// hasNext() 判断是否还有数据
// 在调用 iterator.next() 方法前必须调用 iterator.hasNext() 方法进行检测是否还有下一个元素
// 若不调用 iterator.hasNext(),且没有下一个元素
// 直接调用 iterator.next() 会抛出异常 NoSuchElementException
while(iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
// 当退出 while 循环后,此时的 iterator 指向最后的元素
// 若是再调用 next 方法会抛出异常 NoSuchElementException
// iterator.next(); // 抛出异常
// 如果需要再次遍历,就重置 iterator
iterator = c.iterator();
}
}
2、增强 for
增强 for 的底层还是通过迭代器实现的。
List 接口及其常用方法
List 接口基本介绍
List 接口的常用方法
List 的三种遍历方式
ArrayList 的底层结构和源码分析
transient 表示瞬间,短暂的,被该关键字修饰的属性将不会被序列化。
底层源码分析可通过 debug 看源码可知,参考视频: 视频链接
Vector 的底层结构和源码分析
当有多个线程操作集合时,使用 Vector ,因为它是线程安全(有关键字 synchronized 修饰)的,如果确定只有一个线程操作集合,则用 ArrayList 效率最高(ArrayList 不是线程安全的,没有关键字 synchronized 修饰)。
源码分析查看视频: 视频链接
LinkedList 的底层结构和源码分析
源码分析视频: 视频链接
// 模拟一个简单的双向链表
public class Test {
public static void main(String[] args) {
// 创建三个元素节点
Node node1 = new Node("node1");
Node node2 = new Node("node2");
Node node3 = new Node("node3");
// 连接节点: node1 -> node2 -> node3
node1.next = node2;
node2.next = node3;
// 连接节点: node3 -> node2 -> node1
node3.pre = node2;
node2.pre = node1;
// 让头尾指针指向头尾节点
Node first = node1;
Node last = node3;
// 从头到尾遍历链表
while(true) {
if (first == null) {
break;
}
System.out.println(first);
first = first.next;
}
System.out.println("================");
// 从尾到头变脸链表
while(true) {
if(last == null){
break;
}
System.out.println(last);
last = last.pre;
}
}
}
class Node {
public Object item; // 存放数据区域
public Node pre; // 上一个节点
public Node next; // 下一个节点
public Node(Object item){
this.item = item;
}
@Override
public String toString() {
return "Node{" + "item=" + item + "}";
}
}
List 集合的选择
Set 接口及其常用方法
Set 接口实现类 —— HashSet
源码分析视频: HashSet 源码分析
import java.util.HashSet;
public class Test {
public static void main(String[] args) {
HashSet set = new HashSet();
// add 方法会返回一个 boolean 值,添加成功返回 true ,否则返回 false
// HashSet 集合不能添加重复的元素/对象
System.out.println(set.add("lucy")); // 集合中没有 Lucy,添加成功
System.out.println(set.add("lucy")); // 和上面的 Lucy 重复,添加失败
System.out.println(set.add(new Dog("tom"))); // 添加了一个 tom 的 Dog 对象
System.out.println(set.add(new Dog("tom"))); // 和上面的的 Gog 对象不同,因为是 new 出来的,是一个新的对象,只是恰好重名
System.out.println(set);
// 经典面试题
/*
原因:底层源码在判断两个元素是否相同时,会首先判断哈希值是否相同
如果哈希值相同,在调用 equals 方法(该方法可以有程序员自定义重写)判断两个元素是否相同
如果都相同,就判断是同一个对象,不能加入 HashSet 集合
因为 String 重写了 equals 方法,所以下面内容判定为同一个元素,所以第二个加入 HashSet 集合失败
*/
System.out.println(set.add(new String("zs"))); // 加入成功
System.out.println(set.add(new String("zs"))); // 加入失败,具体原因看底层源码
}
}
class Dog{
private String name;
public Dog(String name){
this.name = name;
}
}
Set 接口实现类 —— LinkedHashSet
Map 接口
视频链接: Map
Map 接口实现类 —— HashMap
上述的 Map 接口都是以 HashMap 为例进行讲解。
Map 接口实现类 —— HashTable
Map 接口实现类 —— Properties
Proterties 继承自 HashTable ,它的 key 和 value 都不允许为 null 。
如何选择对应的集合
TreeSet TreeMap 视频: treeset、treemap
TreeSet 的底层是由 TreeMap 实现的。
Collections 工具类