一. Collection和Map的区别
Collection是一个单列集合,Map是一个双列集合。那么什么是单列集合,什么是双列集合呢?
单列集合,就是每个数据只包含一个值,而双列集合就是每个数据包含两个值(键值对)
二. Collection
(一)Collection集合特点:
List系列集合:添加的元素是有序,可重复,有索引。
- ArrayList,LinkedList: 有序,可重复,有索引
Set系列集合:添加的元素是无序,不重复,无索引。
- HashSet:无序,不重复,无索引
- LinkedHashSet:有序,不重复,无索引
- TreeSet:按照大小默认升序排序,不重复,无索引
(二)Collection的常用方法
1. public boolean add (E e); 添加元素,添加成功返回true 2. public void clear(); 清空集合元素 3. public boolean isEmpty(); 判断集合是否为空,是空返回true 4. public int size(); 获取集合的大小 5. public boolean contains(Object obj); 判断集合中是否包含某个元素 6. public boolean remove(E e); 删除某个元素:如果有多个重复元素默认删除前面的第一个 7. public Object[] toArray(); 把集合转换为数组
import java.util.ArrayList;
import java.util.Collection;
public class CollectionTest2API {
public static void main(String[] args) {
Collection<String> c = new ArrayList<>();
//1. public boolean add (E e); 添加元素,添加成功返回true
c.add("A");
c.add("B");
c.add("C");
c.add("D");
c.add("E");
System.out.println(c);
//2. public void clear(); 清空集合元素
c.clear();
System.out.println(c);
//3. public boolean isEmpty(); 判断集合是否为空,是空返回true
System.out.println(c.isEmpty());
//4. public int size(); 获取集合的大小
System.out.println(c.size());
//5. public boolean contains(Object obj); 判断集合中是否包含某个元素
System.out.println(c.contains("A"));
System.out.println(c.contains("G"));
//6. public boolean remove(E e); 删除某个元素:如果有多个重复元素默认删除前面的第一个
System.out.println(c.remove("A"));
System.out.println(c);
//7. public Object[] toArray(); 把集合转换为数组
Object[] arr = c.toArray();
String[] arr2 = c.toArray(new String[c.size()]); //将集合强制转换为String类型的数组,而非Object类型的数组
System.out.println(Arrays.toString(arr2));
}
}
将一个集合的数据全部倒入到另一个集合中去:
要注意两个集合的数据类型要一致。只是将c2中的数据拷贝并倒入。
Collection<String> c1 = new ArrayList<>();
c1.add("java1");
c1.add("java2");
Collection<String> c2 = new ArrayList<>();
c2.add("java3");
c2.add("java4");
c1.addAll(c2);
(三)Collection的遍历方式
1. 迭代器
迭代器是用来遍历集合的专用方式(数组没有迭代器),在Java中迭代器的代表是Iterator。
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionDemo01 {
public static void main(String[] args) {
Collection<String> c = new ArrayList<>();
c.add("a");
c.add("b");
c.add("c");
c.add("d");
System.out.println(c);
//使用迭代器遍历集合
//1. 从集合对象中获取迭代器对象
Iterator<String> it = c.iterator();
//2. 使用循环结合迭代器遍历集合
while (it.hasNext()) {
String ele = it.next();
System.out.println(ele);
}
}
}
注意,迭代器可以遍历集合,但不能遍历数组
2. 增强for循环
格式:
for (元素的数据类型 变量名:数组或者集合) {
}
- 增强for可以用来遍历集合或者数组
- 增强for遍历集合,本质就是迭代器遍历集合的简化写法
public class CollectionDemo02 {
public static void main(String[] args) {
Collection<String> c = new ArrayList<>();
c.add("a");
c.add("b");
c.add("c");
c.add("d");
System.out.println(c);
//使用增强for遍历集合
for (String ele:c) {
System.out.println(ele);
}
}
}
增强for循环可以用来遍历数组
public class CollectionDemo02 {
public static void main(String[] args) {
//使用增强for遍历数组
String[] names = {"a","b","c","d"};
for (String ele:names) {
System.out.println(ele);
}
}
}
3. Lambda表达式遍历集合
得益于JDK 8开始的新技术Lambda表达式,提供了一种更简单,更方便的遍历方式。
import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Consumer;
public class CollectionDemo03 {
public static void main(String[] args) {
Collection<String> c = new ArrayList<>();
c.add("a");
c.add("b");
c.add("c");
c.add("d");
System.out.println(c);
//default void forEach(Consumer<? super T>action): 结合Lambda表达式遍历集合
c.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
//第一步简化:
c.forEach((String s) -> System.out.println(s));
//第二步简化:
c.forEach(s -> System.out.println(s));
//第三步简化:
c.forEach(System.out::println);
}
}
4. 案例
需求:展示多部电影信息
分析:
- 每部电影都是一个对象,多部电影要使用集合装起来。
- 遍历集合中的3个电影对象,输出每部电影的详情信息。
(四)List集合
List系列集合特点:有序,可重复,有索引。
- List集合因为支持索引,所以多了很多与索引相关的方法,当然,Collection的功能List也都继承了。
1. 常用方法
import java.util.ArrayList;
import java.util.List;
public class ListTest01 {
public static void main(String[] args) {
//1. 创建一个ArrayList集合对象(有序,可重复,有索引)
List<String> list = new ArrayList<>(); //一行经典代码
list.add("a");
list.add("b");
list.add("c");
list.add("d");
System.out.println(list);
//2. public void add(int index, E element): 在某个索引位置插入元素。
list.add(1,"e");
System.out.println(list);
//3. public E remove(int index): 根据索引删除元素,返回被删除元素
System.out.println(list.remove(2));
System.out.println(list);
//4. public E get(int index): 返回集合中指定位置的元素。
System.out.println(list.get(3));
//5. public E set(int index, E element): 修改索引位置处的元素,修改成功后,会返回原来的数据
System.out.println(list.set(2,"g"));
}
}
2. 遍历方式
List集合支持的遍历方式:
- for循环(因为List集合有索引)
- 迭代器
- 增强for循环
- Lambda表达式
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ListTest02 {
public static void main(String[] args) {
//1. 创建一个ArrayList集合对象(有序,可重复,有索引)
List<String> list = new ArrayList<>(); //一行经典代码
list.add("a");
list.add("b");
list.add("c");
list.add("d");
//(1) for循环
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
System.out.println(s);
}
//(2) 迭代器
Iterator<String> it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
//(3) 增强for循环(forEach遍历)
for (String s:list) {
System.out.println(s);
}
//(4) JDK1.8开始之后的Lambda表达式
list.forEach(s->System.out.println(s));
}
}
3. ArrayList集合的底层原理
ArrayList和LinkedList底层采用的数据结构不同,应用场景不同。数据结构就是指存储,组织数据的方式。
- 基于数组实现的 数组的特点:查询速度快(是因为根据索引查询,所以速度快):查询数据通过地址值和索引定位,查询任意数据耗时相同。删除效率低:可能需要把后面很多的数据进行前移。添加效率极低:可能需要把后面很多的数据后移,再添加元素;或者也可能需要进行数组的扩容。
ArrayList适合根据索引查询数据(比如根据随机索引取数据),数据量不是很大的时候。但他不适合数据量大的同时,又要频繁的进行增删操作。
4. LinkedList集合底层原理
基于双链表实现的
链表中的结点是独立的对象,在内存中是不连续的,每个结点包含数据值和下一个结点的地址。
单向链表:
特点1:查询慢,无论查询哪个数据都要从头开始找。
特点2:链表增删相对快
双向链表:
特点:查询慢,增删相对较快,但对首尾元素进行增删改查的速度是极快的
LinkedList新增了很多首位操作的特有方法
LinkedList的应用场景之一:可以用来设计队列
特点:先进先出,后进后出
import java.util.LinkedList;
public class ListTest03 {
public static void main(String[] args) {
//1. 创建一个队列
LinkedList<String> queue = new LinkedList<>();
queue.addLast("第一个人");
queue.addLast("第二个人");
queue.addLast("第三个人");
queue.addLast("第四个人");
System.out.println(queue);
//出队
System.out.println(queue.removeFirst());
System.out.println(queue.removeFirst());
System.out.println(queue.removeFirst());
System.out.println(queue);
}
}
LinkedList的应用场景之一:可以用来设计栈
特点:后进先出,先进后出
只是在首部增删元素,用LinkedList集合实现很适合!
数据进入栈模型的过程称为:压/进栈(push)
数据离开栈模型的过程称为:弹/出栈(pop)
import java.util.LinkedList;
public class ListTest03 {
public static void main(String[] args) {
//2. 创建一个栈对象
LinkedList<String> stack = new LinkedList<>();
//压栈
stack.addFirst("第一颗子弹");
stack.addFirst("第二颗子弹");
stack.addFirst("第三颗子弹");
stack.addFirst("第四颗子弹");
//出栈
System.out.println(stack.removeFirst());
System.out.println(stack.removeFirst());
System.out.println(stack);
}
}
其实我们可以用push和pop来写,更加优雅,实际上调用的还是addFirst,removeFirst
import java.util.LinkedList;
public class ListTest03 {
public static void main(String[] args) {
//2. 创建一个栈对象
LinkedList<String> stack = new LinkedList<>();
//压栈
stack.push("第一颗子弹");
stack.push("第二颗子弹");
stack.push("第三颗子弹");
stack.push("第四颗子弹");
//出栈
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack);
}
}
(五)Set集合
1. 特点:
无序:添加数据的顺序和获取出的数据顺序不一致;不重复,无索引;
- HashSet:无序,不重复,无索引
- LinkedHashSet:有序,不重复,无索引
- TreeSet:排序,不重复,无索引
创建:
set<Integer> set = new HashSet<>(); //创建一个HashSet
set<Integer> set = new LinkedHashSet<>(); //创建一个LinkedHashSet
set<Integer> set = new TreeSet<>(); //创建一个TreeSet
2. HashSet集合的底层原理
在了解HashSet之前,我们需要搞清楚一个前置只是:哈希值
哈希值:
- 就是一个int类型的数值,Java中每个对象都有一个哈希值。
- Java中的所有对象,都可以调用Object类提供的hashCode方法,返回该对象自己的哈希值
public int hashCode() : 返回对象的哈希码值
对象哈希值的特点:
- 同一个对象多次调用hashCode()方法返回的哈希值是相同的
- 不同的对象,他们的哈希值一般不相同,但也有可能会相同(被称为哈希碰撞)
为什么呢?因为哈希值是一个int类型的数据,范围在(-21亿多~21亿多),当对象大于45亿个对象时,哈希值就有可能相同了
HashSet集合的底层原理:
- 基于哈希表实现
- 哈希表是一种增删改茶数据,性能都较好的数据结构
哈希表:
- JDK8之前,哈希表=数组+链表
- JDK8开始,哈希表=数组+链表+红黑树
问:如果数组快占满了,会出现什么问题?该咋办?
答:链表会过长,导致查询性能降低 。扩容。
问:加载因子干啥的?
答:如何判断当前集合是否要扩容了呢?当前长度*加载因子 = 某个值,当这个值填满了就开始扩容。
JDK8开始,当链表长度超过8,且数组长度>=64时,自动将链表转成红黑树
了解一下数据结构(树):
度:每一个节点的子节点数量 (二叉树中,任意节点的度<=2)
树高:树的总层数
根节点:最顶层的节点
左子节点
右子节点
左子树
二叉排序树
规则:小的存左边,大的存右边,一样的不存
二叉查找树存在的问题:
解决办法:平衡二叉树: