一、集合体系概述
集合是一种容器,用来装数据的,类似于数组,但集合的大小可变,开发中也非常常用。集合分为单例集合(collection)和双例集合(Map)
Collection代表单列集合,每个元素(数据)只包含一个值。
Map代表双列集合,每个元素包含两个值(键值对)。
collection分为两种系列的集合:List系列集合(有序、可重复、有索引)、Set系列集合(无序、不重复、无索引)
二、Collection的常用方法
Collection是单列集合的祖宗,它规定的方法(功能)是全部单列集合都会继承的。
常见方法如下:
public class demo01 {
public static void main(String[] args) {
Collection<String> coll = new ArrayList<>();
coll.add("11");
coll.add("aa");
coll.add("2");
coll.add("bb");
System.out.println("此时的集合里面的内容:"+coll);
//判断是否为空
System.out.println(coll.isEmpty());
//查找有几个元素
int size = coll.size();
System.out.println("集合的长度为:"+size);
//存储到数组
Object[] arr = coll.toArray();
System.out.println("转化为数组之后:"+Arrays.toString(arr));
//删除一个
boolean remove = coll.remove("aa");
System.out.println("是否删除成功:"+remove+"删除之后的内容:"+coll);
//判断是否包含某个对象
boolean bb = coll.contains("bb");
System.out.println("是否含有bb:"+bb);
//清空所有的
coll.clear();
System.out.println("清楚所有之后的:"+coll);
}
三、Collection的遍历方式
3.1 迭代器
迭代器是用来遍历集合的专用方式(数组没有迭代器),在Java中迭代器的代表是Iterator。
- 获取迭代器对象 Iterator<泛型> it = 集合.iterator();
- 使用 while循环遍历 ,条件 it.hasNext()
- 在循环中获取数据(指针指向下一个元素)
Demo
public class Demo01 {
//目标:掌握迭代器的方式遍历集合
public static void main(String[] args) {
//创建学生对象
Student student1 = new Student("张三",22);
Student student2 = new Student("李四",21);
Student student3 = new Student("王五",25);
//把学生对象存储到集合中
Collection<Student> coll = new ArrayList<>();
//给集合赋值
coll.add(student1);
coll.add(student2);
coll.add(student3);
//迭代器遍历
Iterator<Student> iterator = coll.iterator();
while (iterator.hasNext()){//hasNext()判断集合的内容是否为空
Student s = iterator.next();//获取到对应的元素
System.out.println(s);
}
}
}
3.2 增强for循环
增强for可以用来遍历集合或者数组。
增强for遍历集合,本质就是迭代器遍历集合的简化写法。
格式:for(数据类型 变量名 : 数组\集合) {}
注:修改增强for中的变量值不会影响到集合中的元素。
public class Demo02 {
/*
目标:使用增强for循环来遍历 数组/集合
底层原理就是 迭代器
*/
public static void main(String[] args) {
Collection<String> coll = new ArrayList<>();
coll.add("张三");
coll.add("李四");
coll.add("王五");
// 集合/数组名.for 快捷创建
for (String s : coll) {
System.out.println(s);
s += "hello";//只是个临时变量,并不会改变上面集合里面的内容
System.out.println(s);
}
System.out.println("集合内容:"+coll);
}
}
3.3 lambda表达式
得益于JDK 8开始的新技术Lambda表达式,提供了一种更简单、更直接的方式来遍历集合。使得代码更加的简洁
Demo
public class Demo03 {
/*
目标:lambda表达式 循环遍历集合\数组
*/
public static void main(String[] args) {
Collection<String> coll = new ArrayList<>();
coll.add("张三");
coll.add("李四");
coll.add("王五");
//匿名内部类
coll.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
System.out.println("----------------------------");
//lambda 表达式
coll.forEach((s) -> {
System.out.println(s);//标黄表示还可以用简便的方法
});
System.out.println("-----------------------------------");
//方法调用,对象名::类名 soutc 快捷键直接生成
coll.forEach(System.out::println);
}
}
四、List集合
4.1特点、特有方法
ArrayList:有序,可重复,有索引。
LinkedList:有序,可重复,有索引
底层的代码实现不同,使用使用这两个的使用场景是不同的。
提供了四种方法:总结来说就是增删改查
4.2遍历方式
普通for循环(因为list集合是有序、有索引的)、迭代器、增强for循环、Lambda表达式
public class Demo01 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("张三");
list.add("王虎");
list.add("皮特");
list.add("白帝");
System.out.println("--------普通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()){
String s = it.next();
System.out.println(s);
}
System.out.println("---------加强for循环--------");
for (String s : list) {
System.out.println(s);
}
System.out.println("-----------lambda---------");
list.forEach((s)->{
System.out.println(s);
});
System.out.println("----------方法引用----------");
list.forEach(System.out::println);
}
}
4.3底层原理比较
ArrayList集合的底层原理:ArrayList的底层是数组,对此查询快(根据索引),增删慢,因为增删会对数组中的数据进行移动
使用场景:大量数据的查询、少量数据的增删,不适合大量的数据,并且需要进行频繁的增删操作。
LinkedList集合的底层原理:基于双链表实现的,链表中的结点是独立的对象,在内存中是不连续的,每个结点包含数据值和下一个结点的地址。因此查询慢、增删快,但对首尾元素进行增删改查的速度是极快的。
使用场景:设计队列、栈
Demo
public class demo03 {
/*
LinkedList 去 模拟队列 和 栈
队列 入队:queue.addLast 出队:queue.removeFirst
栈 入栈: stack.addFirst 出栈: stack.removeFirst
*/
public static void main(String[] args) {
LinkedList<String> queue = new LinkedList<>();
//入队
queue.addLast("第一个人");
queue.addLast("第二个人");
queue.addLast("第三个人");
queue.addLast("第四个人");
//出栈
System.out.println(queue.removeFirst());
System.out.println(queue.removeFirst());
System.out.println(queue.removeFirst());
System.out.println(queue.removeFirst());
System.out.println("---------------------------------");
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.removeFirst());
System.out.println(stack.removeFirst());
}
}
五、Set集合
5.1Set集合特点:
无序(添加数据的顺序和获取出的数据顺序不一致;)、不重复、无索引
HashSet : 无序、不重复、无索引。
LinkedHashSet:有序、不重复、无索引。
TreeSet:排序、不重复、无索引。
Demo
public class Demo01 {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
set.add("11");
set.add("11");
set.add("aa");
set.add("手机");
set.add("22");
set.add("a2023");
System.out.println(set);
//遍历获取每一个 迭代器
Iterator<String> it = set.iterator();
while (it.hasNext()){
String s = it.next();
if (s.length()>2)
System.out.println(s);
}
System.out.println("--------------------------------");
Set<String> linked = new LinkedHashSet<>();
linked.add("11");
linked.add("11");
linked.add("aa");
linked.add("手机");
linked.add("22");
linked.add("a2023");
System.out.println(linked);
//lambda 表达式
linked.forEach((l)->{
System.out.println(l);
});
System.out.println("------------------------------");
Set<Integer> treeSet = new TreeSet<>();
treeSet.add(11);
treeSet.add(122);
treeSet.add(131);
treeSet.add(1);
treeSet.add(4);
treeSet.add(14);
treeSet.add(100);
treeSet.add(99);
treeSet.add(44);
//加强for循环
for (Integer integer : treeSet) {
System.out.println(integer);
}
System.out.println(treeSet);
}
}
package com.itheima.demo04_set;
import java.util.Objects;
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "Student{name = " + name + ", age = " + age + "}";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
public class Demo03 {
public static void main(String[] args) {
User user1 = new User("张三",177.5,23);
User user2 = new User("李四",172.7,24);
User user3 = new User("王五",173.6,21);
User user4 = new User("皮特",177.5,22);
TreeSet<User> treeSet = new TreeSet<>();
//2.通过调用TreeSet集合有参数构造器,可以设置Comparator对象(比较器对象,用于指定比较规则)。
/*TreeSet<User> treeSet = new TreeSet<>(new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
return Double.compare(o1.getHeight(),o2.getHeight());
}
});*/
treeSet.add(user1);
treeSet.add(user2);
treeSet.add(user3);
treeSet.add(user4);
//此时输出的报错的 需要进行
//1.Comparable接口,重写里面的compareTo方法来指定比较规则。
System.out.println(treeSet);
}
}
5.2HashSet集合的底层原理
在jdk8之前:哈希表=数组+列表
Jdk8之后:哈希表=数组+列表+红黑树
添加相同数据时候,需要重写重写对象的hashCode和equals方法。
LinkedHashSet集合的底层原理
依然是基于哈希表(数组、链表、红黑树)实现的。但是,它的每个元素都额外的多了一个双链表的机制记录它前后元素的位置
添加相同数据时候,需要重写重写对象的hashCode和equals方法。可以指定排序的规则
5.3TreeSet集合
特点:不重复、无索引、可排序(默认升序排序 ,按照元素的大小,由小到大排序)
底层是基于红黑树实现的排序。
排序规则:
对于数值类型:Integer , Double,默认按照数值本身的大小进行升序排序。
对于字符串类型:默认按照首字符的编号升序排序。
对于自定义类型如Student对象,TreeSet默认是无法直接排序的。
自定义排序
Demo
类里面重写equals()、hashCode()方法
六、注意事项:集合的并发修改异常问题
集合的并发修改异常
使用迭代器遍历集合时,又同时在删除集合中的数据,程序就会出现并发修改异常的错误。
由于增强for循环遍历集合就是迭代器遍历集合的简化写法,因此,使用增强for循环遍历集合,又在同时删除集合中的数据时,程序也会出现并发修改异常的错误
怎么保证遍历集合同时删除数据时不出bug?
使用迭代器遍历集合,但用迭代器自己的删除方法删除数据即可。
如果能用for循环遍历时:可以倒着遍历并删除;或者从前往后遍历,但删除元素后做i --操作。