Collection集合体系概述:
Collection集合特点
list系列集合:
- 添加的元素是有序,可重复,有索引
- ArrayList、LinekdList:有序,可重复,有索引
Set系列集合:
- 添加的元素是无序,不重复,无索引
- HashSet:无序,不重复,无索引
- LinedHashSet:有序,不重复,无索引
- TreeSet: 按照大小默认升序排序,不重复,无索引
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
public class ColletionTest1 {
public static void main(String[] args) {
//简单确认一下Colletion集合的特点
ArrayList<String> list = new ArrayList<>();//有序 可重复 有索引
list.add("java1");
list.add("java2");
list.add("java1");
list.add("java3");
list.add("java2");
System.out.println(list);
HashSet<String> set = new HashSet<>();//无序 不重复 无索引
set.add("java1");
set.add("java3");
set.add("java2");
set.add("java4");
set.add("java5");
set.add("java1");
System.out.println(set);
}
}
Collection的常用方法:
代码演示:
import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; public class ColletionTest2 { public static void main(String[] args) { //add()添加元素 Collection<String> c = new ArrayList<>(); c.add("java1"); c.add("java1"); c.add("java2"); c.add("java2"); c.add("java3"); System.out.println(c); //clear()清空集合的所有元素 c.clear(); System.out.println(c); //isEmpty判断集合是否为空,是返回true 反之 System.out.println(c.isEmpty()); //size()获取集合的大小 System.out.println(c.size()); //contains()判断集合钟是否有某个元素,是返回true,反之 c.add("java2"); c.add("java3"); c.add("java2"); System.out.println(c.contains("java2")); System.out.println(c.contains("java1")); //remove()删除某个元素,如果多个重复元素默认删除前面第一个 System.out.println(c.remove("java2")); System.out.println(c); //toArray()集合转成数组 Object[] arr = c.toArray(); System.out.println(Arrays.toString(arr)); //用指定数组接收 String[] arr1 = c.toArray(new String[0]); System.out.println(Arrays.toString(arr1)); System.out.println("=================================="); //把一个集合的全部数据倒入另一个集合中去 Collection<String> c1 = new ArrayList<>(); c1.add("java1"); c1.add("java2"); Collection<String> c2 = new ArrayList<>(); c1.add("java3"); c1.add("java4"); c1.addAll(c2); //就是c2集合全部数据copy一份到c1集合中去 System.out.println(c1); } }
Collection的遍历方式:
迭代器:
概述:
迭代器是用来遍历集合的专用方式(数组没有迭代器)在java中迭代器的代表就是:Iterator
Collection集合获取迭代器的方法:
public class CollectionDemo1 {
public static void main(String[] args) {
Collection<String> c = new ArrayList<>();
c.add("赵敏");
c.add("赵三");
c.add("李四");
c.add("王五");
System.out.println(c);
//使用迭代器遍历集合
//1、从结合对象中获取迭代器对象
Iterator<String> it = c.iterator();
System.out.println(it.next());
System.out.println(it.next());
System.out.println(it.next());
System.out.println(it.next());
//System.out.println(it.next());多取就会报错
}
}
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionDemo1 {
public static void main(String[] args) {
Collection<String> c = new ArrayList<>();
c.add("赵敏");
c.add("赵三");
c.add("李四");
c.add("王五");
System.out.println(c);
//使用迭代器遍历集合
//2、使用循环结合迭代器遍历集合
Iterator<String> it = c.iterator();
while (it.hasNext()){
String ele = it.next();
System.out.println(ele);
//简化版
System.out.println(it.next());
}
}
}
增强for:
既能遍历集合也可以遍历数组
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionDemo1 {
public static void main(String[] args) {
Collection<String> c = new ArrayList<>();
c.add("赵敏");
c.add("赵三");
c.add("李四");
c.add("王五");
System.out.println(c);
//使用迭代器遍历集合
//2、使用增强for循环遍历集合
for(String ele : c){
System.out.println(ele);
}
//也可以遍历数组
String[] names = {"a","b","c","d"};
for (String name : names) {
System.out.println(name);
}
}
}
lambda表达式:
得益于JDK8开始的新技术Lambda表达式,提供了一种更简单、更直接的方式来遍历集合。
import java.lang.invoke.LambdaConversionException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.function.Consumer;
public class CollectionDemo1 {
public static void main(String[] args) {
Collection<String> c = new ArrayList<>();
c.add("赵敏");
c.add("赵三");
c.add("李四");
c.add("王五");
System.out.println(c);
//使用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(s -> System.out.println(s));
*/
c.forEach(System.out::println);
}
}
案例:遍历集合中的自定义对象
- 需求:
- 展示多部电影信息
- 分析:
- 每部电影都是一个对象,多部电影要使用集合装起来。
- :遍历集合中的3个电影对象,输出每部电影的详情信息。
import java.util.ArrayList;
import java.util.Collection;
public class CollectionDemo1 {
public static void main(String[] args) {
//创建一个集合容器负责储蓄多部电影对象
Collection<Movie> c = new ArrayList<>();
c.add(new Movie("《肖生克的救赎》", 9.7 , "罗宾斯"));
c.add(new Movie("《霸王别姬》", 9.6 , "张国荣、张丰毅"));
c.add(new Movie("《阿甘正传》", 9.5 , "汤姆~汉克斯"));
System.out.println(c);//集合存储对象的地址
for (Movie movie : c) {
System.out.println("电影名>>>"+movie.getName());
System.out.println("电影评分>>>"+movie.getScore());
System.out.println("电影主演>>>"+movie.getActor());
System.out.println("===============================");
}
}
}
public class Movie {
private String name;
private double score;
private String actor;
public Movie() {
}
public Movie(String name, double score, String actor) {
this.name = name;
this.score = score;
this.actor = actor;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
public String getActor() {
return actor;
}
public void setActor(String actor) {
this.actor = actor;
}
}
list集合
List集合特有方法
list集合因为支持索引,所有多了很多索引有关的方法,当然,collection的功能list也都继承了
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class CollectionDemo1 {
public static void main(String[] args) {
//创建一个ArrayList集合对象(有序,可重复,有索引)
List<String> list = new ArrayList<>();//一行经典代码
list.add("蜘蛛精");
list.add("至尊宝");
list.add("牛默认");
list.add("芭蕉扇");
list.add("老佛祖");
System.out.println(list);
//根据某个索引位置插入元素
list.add(2,"紫霞仙子");
System.out.println(list);
//根据索引删除元素,返回删除元素
System.out.println(list.remove(3));
System.out.println(list);
//修改索引位置处的元素,返回原来的数据
System.out.println(list.set(3,"变态"));
System.out.println(list);
}
}
遍历方式:
- for循环(因为有索引)
- 迭代器
- 增强for循环
- lambda表达式
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
public class CollectionDemo1 {
public static void main(String[] args) {
//创建一个ArrayList集合对象(有序,可重复,有索引)
List<String> list = new ArrayList<>();//一行经典代码
list.add("蜘蛛精");
list.add("至尊宝");
list.add("牛默认");
list.add("芭蕉扇");
list.add("老佛祖");
System.out.println(list);
//for循环
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
System.out.println("=======================");
//迭代器
Iterator<String> it = list.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
System.out.println("=======================");
//增强for循环
for (String s : list) {
System.out.println(s);
}
System.out.println("=======================");
//JDK 1.8开始之后 Lambda表达式
list.forEach(s -> {
System.out.println(s);
});
}
}
ArrayList集合的底层逻辑:
ArrayList集合的底层原理:
- 基于数组实现的
- 查询速度快
- 删除效率低
- 添加效率极低
- 利用无参构造器创建的集合,会在底层创建一个默认长度为0的数组
- 添加第一个元素时,底层会创建一个新的长度为10 的数组
- 存满时,会扩容1.5倍
- 如果一次添加多个元素,1.5倍还放不下,则新建数组的长度以实际为准
- 根据索引查询的数组,适合
- 数据量大,又频繁的进行增删操作的,不适合
linkedList集合的底层原理:
- 基于双链表实现的
什么是链表?有啥特点?
- 链表中的节点是独立的对象,在内存中是不连续的,每个结点包含数据值和下一个结点的地址
- 查询慢,无论查询哪个数据都要从头开始找
- 增删相对数组是快的
- 双链表对首尾元素进行增删改查的速度是极快的
应用场景:
可以用来设计队列
import java.util.*;
public class CollectionDemo1 {
public static void main(String[] args) {
//创建一个队列
LinkedList<String> queve = new LinkedList<>();
queve.addLast("1号");
queve.addLast("2号");
queve.addLast("3号");
queve.addLast("4号");
queve.addLast("5号");
System.out.println(queve);
//出队
System.out.println(queve.removeFirst());
System.out.println(queve.removeFirst());
System.out.println(queve.removeFirst());
System.out.println(queve);
}
}
可以用来设计栈
import java.util.*;
public class CollectionDemo1 {
public static void main(String[] args) {
//创建一个栈对象
LinkedList<String> stack = new LinkedList<>();
//压栈(push和addFirst一样的)
stack.push("第1颗子弹");
stack.push("第2颗子弹");
stack.push("第3颗子弹");
stack.push("第4颗子弹");
stack.push("第5颗子弹");
System.out.println(stack);
//出栈(pop)
System.out.println(stack.removeFirst());
System.out.println(stack.removeFirst());
System.out.println(stack.removeFirst());
System.out.println(stack);
}
}
set集合
set系列集合特点:
实现类 | 特点 |
HashSet | 无序 不重复 无索引 |
LinkedHashSet | 有序 不重复 无索引 |
TreeSet | 排序 不重复 无索引 |
import java.util.*;
public class CollectionDemo1 {
public static void main(String[] args) {
//创建set集合的对象
//Set<Integer> set = new HashSet<>();//无序 不重复 无索引
Set<Integer> set = new TreeSet<>();//排序(升序) 不重复 无索引
set.add(666);
set.add(622);
set.add(633);
set.add(644);
set.add(644);
set.add(655);
System.out.println(set);
}
}
HashSet集合的底层原理
哈希值:
- 就是一个int类型的数值,java中每个对象都有一个哈希值
- java中的所有对象都可以调用object类提供的HashCode方法,返回自己的哈希值
对象哈希值的特点:
- 同一个对象多次调用hashCode()方法返回的哈希值是相同的。
- 不同的对象,他们的哈希值一般不相同,但也有可能会相同(哈希碰撞)
import java.util.*;
public class CollectionDemo1 {
public static void main(String[] args) {
Movie movie1 = new Movie("《熊出没》",9.8,"光头强");
Movie movie2 = new Movie("《喜马拉雅山》",9.8,"中国出版");
System.out.println(movie1.hashCode());
System.out.println(movie1.hashCode());
System.out.println(movie2.hashCode());
String str1 = new String("abc");
String str2 = new String("acD");
//哈希碰撞
System.out.println(str1.hashCode());
System.out.println(str2.hashCode());
}
}
HashSet集合的底层原理:
- 基于哈希表实现的
- 哈希表是一种增删改查数据
哈希表是啥?
- JDK8之前,哈希表 = 数组+链表
- 创建一个默认长度为16的数组,默认加载因子为0.75,数组名叫table
- 使用元素的哈希值对数组的长度求余计算出应存入的位置
- 判断当前位置是否null,如果null直接存入,否则就调用equals方法比较,相等则不存,不相等则存入数组
- JDK8之前新元素存入数组,占据老元素位置,老元素挂下面
- JDK8开始之后,新元素直接挂在老元素下面
- JDK8开始,哈希表= 数组+链表+红黑树
- 哈希表是一种增删改查数据性能都比较好的结构
了解一下数据结构(树):
-
二叉树
- 二叉查找树
- 平衡二叉树
- 红黑树
深入理解HashSet集合去重复的机制:
hashset集合默认不能对内容一样的两个不同对象去重复!
如何希望set集合认为两个内容一样的对象是重复的,必须重写对对象的hashcode()和equals()方法
import java.util.*;
public class CollectionDemo1 {
public static void main(String[] args) {
Movie movie1 = new Movie("《熊出没》",9.8,"光头强");
Movie movie2 = new Movie("《熊出没》",9.8,"光头强");
System.out.println(movie1.hashCode());
System.out.println(movie2.hashCode());
}
}
import java.util.Objects;
public class Movie {
private String name;
private double score;
private String actor;
public Movie() {
}
public Movie(String name, double score, String actor) {
this.name = name;
this.score = score;
this.actor = actor;
}
//只要两个对象内容一样,就返回true
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Movie movie = (Movie) o;
return Double.compare(score, movie.score) == 0 && Objects.equals(name, movie.name) && Objects.equals(actor, movie.actor);
}
@Override
public int hashCode() {
return Objects.hash(name, score, actor);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
public String getActor() {
return actor;
}
public void setActor(String actor) {
this.actor = actor;
}
}
LinkedHashSet底层逻辑
- 依然是基于哈希表(数组,链表,红黑树)实现的
- 但是,它的每个元素都额外的多了一个双链表的机制记录它前后元素的位置
TreeSet
- 特点:不重复,无索引,可排序(默认升序排序,按照元素的大小,由小到大排序)。
- 底层是基于红黑数实现的排序。
- 对于自定义类型如student对象,TreeSet默认是无法直接排序的。
自定义排序规则:
- TreeSet集合存储自定义类型的对象时,必须指定排序规则,支持如下两种方式来指定比较规则
- 方式一:
- 让自定义的类(比如学生类),实现comparable接口,重写在里面的compareTo方法来指定比较规则。
- 方法二:
- 通过调用TreeSet集合有参数构造器,可以设置Comparator对象,(比较器对象,用于指定比较规则。)
总结:
1、如果希望记住元素的添加顺序,需要储蓄重复的元素,又要频繁的根据索引查询数据?
- 用ArrayList集合(有序,可重复,有索引),底层基于数组的。(常用)
2,如果希望记住元素的添加顺序,且增删首尾数据的情况增多?
- 用LinkedList集合(有序,可重复,有索引),底层基于双链表实现的。
3,如果不在意元素顺序,也没有重复元素需要存储,只希望增删改查都快?
- 用HashSet集合(无序,不重复,无索引),底层基于哈希表实现的
4,如果希望记住元素的添加顺序,也没有重复元素需要存储,且希望增删改查都快?
- LinkedHashSet集合(有序,不重复,无索引),底层基于哈希表,和双链表
5,如果要对元素进行排序,也没有重复元素需要存储?且希望增删改查都快?
- 用TreeSet集合,基于红黑树实现。
注意事项,集合的并发修改异常问题
- 使用迭代器遍历集合时,又同时在删除集合中的数据,程序就会出现并发修改异常的错误。
- 解决方案:用for循环,删除数据时候,将索引-1,或用迭代器自己的remove,删除迭代器当前遍历的数据(使用增强for循环是没办法解决这个bug,lambda也一样不行)