概念:
集合是java中提供的一种容器,可以用来存储多个数据
集合和数组的区别:
- 数组的长度是固定的,集合的长度是可变的。
- 数组中存储的是同一类型的元素,可以存储任意类型数据。集合存储的都是引用数据类型。如果想存储基本类型数据需要存储对应的包装类型。集合的底层也是数组。
- 集合底层是数组,在创建集合的时,会创建一个长度为10的数组,如果长度不够就会自动扩容原来数组长度的1.5倍,也就是15,然后将原数组拷贝的数据拷贝到长度15的数组里。
集合的体系结构:
Collection:
是单列集合的顶层接口,JDK 不提供此接口的任何直接实现,它提供更具体的子接口(如Set和List)实现
创建对象方式:
多态的方式
具体的实现类ArrayList
Collection常用方法:
方法名 | 说明 |
---|---|
boolean add(E e) | 添加元素 |
boolean remove(Object o) | 从集合中移除指定的元素 |
boolean removeIf(Object o) | 根据条件进行移除 |
void clear() | 清空集合中的元素 |
boolean contains(Object o) | 判断集合中是否存在指定的元素 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中元素的个数 |
public static void main(String[] args) {
Collection<String> arrayList = new ArrayList<>();
// 添加
arrayList.add("itzhuzhu");
arrayList.add("hehe");
arrayList.add("今天过生日");
// 删除
System.out.println(arrayList.remove("itzhuzhu"));
System.out.println(arrayList.remove("这里删除一段不存在的看一下返回的结果"));
// 根据条件删除
// removeIf底层会遍历集合,s代表每个元素,true删除,false不删除
arrayList.removeIf((String s) -> {
return s.length() == 4;
});
// 清空集合所有元素
arrayList.clear();
// 判断集合中是否存在指定的元素
System.out.println("contains: " + arrayList.contains("hehe"));
// 判断集合是否为空
System.out.println("isEmpty: " + arrayList.isEmpty());
// 返回集合的长度
System.out.println("arrayList:" + arrayList.size());
System.out.println(arrayList);
}
iterator:
- iterator是迭代器,集合专属遍历方式
- 迭代器原理,默认指向0索引,依次按照需求操作,但是每次都会先–,再操作,对比fori循环先++,可以解决相同邻近元素无法删除操作。
循环执行:
- 先根据当前位置判断有没有下一个
- 获取当前位置的元素,再指向下一个
为什么要用迭代器:
在程序开发中,经常需要遍历集合中的所有元素。针对这种需求,JDK专门提供了一个接口 java.util.Iterator 。
迭代器怎么获取:
Iterator 对象 = 集合对象.iterator()
迭代器的实现原理
- 当遍历集合时,首先通过调用t集合的iterator()方法获得迭代器对象,然后使用hashNext()方法判断集合中是否存在下一个元素,如果存在,则调用next()方法将元素取出,否则说明已到达了集合末尾,停止遍历元素。
- Iterator迭代器对象在遍历集合时,内部采用指针的方式来跟踪集合中的元素。
常用方法:
方法名 | 说明 |
---|---|
Iterator iterator() | 创建迭代器,默认指向0索引 |
boolean hasNext() | 判断当前位置是否有元素可以被取出 |
E next() | 获取当前位置的元素,将迭代器对象移向下一个索引位置 |
void remove() | 删除迭代器对象当前指向的元素 |
public static void main(String[] args) {
//创建集合对象
Collection<String> arrayList = new ArrayList<>();
//添加元素
arrayList.add("hello");
arrayList.add("world");
arrayList.add("world");
// 获取迭代器对象
Iterator<String> iterator = arrayList.iterator();
while (iterator.hasNext()) {
String next = iterator.next();
if ("world".equals(next)) {
/** 不使用remove要手动去判断,当两个相邻的元素一样就会删除不了,因为循环判断以后就会做++,
集合会自动加减长度,假如索引1和2都是a,那么删除了索引1,集合就会把索引2的a挪到1
*/
iterator.remove();
}
System.out.println("next = " + next);
}
System.out.println("arrayList = " + arrayList);
}
增强for :
- 增强for是JDK5之后出现的,其内部原理是一个Iterator迭代器
- 实现Iterable接口的类才可以使用迭代器和增强for,只要单列集合才可以使用,双列的都不可以。
- 简化数组和Collection集合的遍历
格式:
for(集合/数组中元素的数据类型 变量名 : 集合/数组名) {
// 已经将当前遍历到的元素封装到变量中了,直接使用变量即可
}
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
list.add("e");
list.add("f");
for (String str : list) {
// 在集合里修改第三方变量值可以实现,但是不会影响集合原来的值
str = "hehe";
System.out.println("str = " + str);
}
System.out.println("list = " + list);
}
三种循环的使用场景:
- 需要操作索引就用普通for循环
- 只需要遍历使用增强for
- 遍历的过程中需要删除元素,用迭代器
代码演示:
public static void main(String[] args) {
// 我们学习Collection集合中的方法,编译看左边,我们使用的方法都是来自Collection接口
// 多态
// 接口 变量名 = new 实现类();
Collection<String> coll = new ArrayList<>();
// 1.boolean add(E e) 往集合中添加一个元素
coll.add("王宝强");
coll.add("谢霆锋");
coll.add("贾乃亮");
coll.add("陈羽凡");
System.out.println(coll);
// 2.void clear() 从此集合中删除所有元素
// coll.clear();
// 3.boolean contains(Object o) 判断集合中是否包含指定的元素
System.out.println(coll.contains("贾乃亮")); // true
System.out.println(coll.contains("杜海涛")); // false
// 4.boolean isEmpty() 判断集合是否为空(没有元素),如果为空返回true
System.out.println(coll.isEmpty());
// 5.boolean remove(Object o) 删除集合中的指定元素,如果删除返回true
System.out.println(coll.remove("王宝强")); // true
System.out.println(coll.remove("大郎")); // false
// 6.int size() 返回此集合中的元素数
System.out.println(coll.size());
System.out.println(coll);
System.out.println("------");
// 7.Object[] toArray() 将集合转成数组
Object[] objects = coll.toArray();
for (int i = 0; i < objects.length; i++) {
Object obj = objects[i];
System.out.println(obj);
}
}
List集合:
有序集合,这里的有序指的是存取顺序
可以精确控制列表中每个元素的插入位置,可以通过整数索引访问元素,并搜索列表中的元素,与Set集合不同,列表通常允许重复的元素
常用的子类:
- ArrayList
- LinkedList
特点:
- 存取有序
- 可以重复
- 有索引
List集合的特有功能
方法名 | 描述 |
---|---|
void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
E get(int index) | 返回指定索引处的元素 |
代码演示:
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
// void add(int index,E element) 在此集合中的指定位置插入指定的元素
list.add(0,"a");
list.add("b");
list.add("c");
list.add("a");
// E remove(int index) 删除指定索引处的元素,返回被删除的元素
list.remove(0);
list.remove("b");
System.out.println("list = " + list);
// E set(int index,E element) 修改指定索引处的元素,返回被修改的元素
list.set(1, "修改了索引1");
// E get(int index) 返回指定索引处的元素
System.out.println("索引1 = " + list.get(1));
System.out.println("list = " + list);
}
ArrayList:
底层是数组结构实现,查询快、增删慢
LinkedList集合的特有功能:
底层是双向链表结构实现,查询慢、增删快
方法名 | 说明 |
---|---|
public void addFirst(E e) | 在该列表开头插入指定的元素 |
public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
public E getFirst() | 返回此列表中的第一个元素 |
public E getLast() | 返回此列表中的最后一个元素 |
public E removeFirst() | 从此列表中删除并返回第一个元素 |
public E removeLast() | 从此列表中删除并返回最后一个元素 |
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
list.add("a");
list.add("b");
list.add("c");
// public void addFirst(E e) 在该列表开头插入指定的元素
list.addFirst("-a");
// public void addLast(E e) 将指定的元素追加到此列表的末尾
list.addLast("d");
System.out.println("操作前:" + list);
// public E getFirst() 返回此列表中的第一个元素
System.out.println("第一个元素 :" + list.getFirst());
// public E getLast() 返回此列表中的最后一个元素
System.out.println("最后一个元素 : " + list.getLast());
// public E removeFirst() 从此列表中删除并返回第一个元素
System.out.println("删除第一个元素 : " + list.removeFirst());
// public E removeLast() 从此列表中删除并返回最后一个元素
System.out.println("删除最后一个元素() : " + list.removeLast());
System.out.println("操作后 : " + list);
}
Set集合:
- 不可重复
- 存取无序
- 无索引不能使用普通for循环遍历
TreeSet:
- 不可以存储重复元素
- 没有索引
- 可以将元素按照规则进行排序
(使用TreeSet必须定义排序规则)
- TreeSet底层实现的是红黑树的数据结构
实现原理:
自然排序Comparable:
Comparator是一个比较接口,需求需要实现比较就要实现该接口,并重写compareTo方法
实现步骤:
- 使用空参构造创建TreeSet集合
用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的- 自定义类实现Comparable接口
自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(T o)方法- 重写接口中的compareTo方法
重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
返回值规则:
- 如果返回值为负数,表示当前存入的元素是较小值,存左边
- 如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存
- 如果返回值为正数,表示当前存入的元素是较大值,存右边
- 排序在使用的时候,默认使用自然排序,当自然排序不满足现在的需求时,必须使用比较器排序,比如要按照字符串排序就可以用compare调用length排序
compareTo:
- int compareTo(T o):将此对象与指定的对象进行比较以获得顺序。 返回负整数,零或正整数,因为此对象小于,等于或大于指定对象。
- 用于字符串与对象进行比较
- 按字典顺序比较两个字符串
代码演示:
学生类
public class Student implements Comparable<Student> {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Student o) {
// 主要判断条件:按照年龄排序
int result = this.age - o.age;
//次要判断条件: 年龄相同时,按照姓名的字母顺序排序,这里的compareTo是String类的
result = result == 0 ? this.name.compareTo(o.getName()) : result;
return result;
}
}
测试类
public class Demo {
public static void main(String[] args) {
// 创建集合
TreeSet<Student> ts = new TreeSet<>();
// 存入学生
Student s1 = new Student("zhangsan", 28);
Student s2 = new Student("lisi", 27);
Student s3 = new Student("wangwu", 29);
Student s4 = new Student("zhaoliu", 28);
Student s5 = new Student("qianqi", 30);
// 添加学生到集合
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
// 遍历集合
for (Student student : ts) {
System.out.println(student);
}
}
}
比较器排序comparator:
此比较器可以比较的对象类型,Comparator最常用的场景就是排序和分组
使用步骤:
创建TreeSet对象的时候传递comparator的实现类对象,重写compare方法,根据返回值进行排序
返回值规则:
- 如果返回值为负数,表示当前存入的元素是较小值,存左边
- 如果返回值为0,表示当前存入的元素跟集合中元素重复了,不存
- 如果返回值为正数,表示当前存入的元素是较大值,存右边
- 排序在使用的时候,默认使用自然排序,当自然排序不满足现在的需求时,必须使用比较器排序,比如要按照字符串排序就可以用compare调用length排序
代码演示:
老师类
public class Teacher {
private String name;
private int age;
public Teacher() {
}
public Teacher(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
测试类
public class TeacherTest {
public static void main(String[] args) {
// 创建集合
TreeSet<Teacher> ts = new TreeSet<>(new Comparator<Teacher>() {
@Override
public int compare(Teacher o1, Teacher o2) {
//o1表示现在要存入的那个元素 o2表示已经存入到集合中的元素
//主要条件
int result = o1.getAge() - o2.getAge();
//次要条件
result = result == 0 ? o1.getName().compareTo(o2.getName()) : result;
return result;
}
});
// 存入老师
Teacher s1 = new Teacher("zhangsan", 28);
Teacher s2 = new Teacher("lisi", 27);
Teacher s3 = new Teacher("wangwu", 29);
Teacher s4 = new Teacher("zhaoliu", 28);
Teacher s5 = new Teacher("qianqi", 30);
// 添加老师到集合
ts.add(s1);
ts.add(s2);
ts.add(s3);
ts.add(s4);
ts.add(s5);
// 遍历集合
for (Teacher teacher : ts) {
System.out.println(teacher);
}
}
}
排序总结:
排序的时候想好主要条件和次要提交条件,这样做比较严谨
HashSet:
- 底层数据结构是哈希表
- 存取无序
- 不可以存储重复元素
- 没有索引,不能使用普通for循环遍历
- 属于Set集合所以元素唯一
- 底层是哈希表结构的
哈希值:
哈希值是JDK根据对象的
地址
或者属性值
,算出来的int类型的数值
获取哈希值:
Object类中的 public int hashCode():返回对象的哈希码值
哈希值的特点:
- 如果没有重写hashCode方法,那么是根据对象的地址值计算出的哈希值。
- 同一个对象多次调用hashCode()方法返回的哈希值是相同的
- 不同的对象的哈希值是不同的
- 如果重写hashCode方法,一般是通过对象的属性值计算出哈希值
- 如果不同的对象属性值是一样的,那么计算出来的哈希值也是一样的
哈希表结构:
JDK1.8以前底层是数组+链表:
- 创建一个默认长度为16,加载因子(决定哈希表什么扩容)为0.75的数组,数组名为table
- 根据元素的哈希值跟数组的长度计算出应存入的位置
- 判断当前位置是否为null,如果是null直接存入
- 如果应存入的位置不为null,表示有元素,则调用equals方法比较属性值
- 如果一样,则不存,如果不一样,则存入数组,老元素挂在新元素下面
JDK1.8以后是数组+链表+红黑树:
节点个数少于等于8个就用(数组 + 链表),多于8个用(数组 + 红黑树)
练习:
创建一个存储学生对象的集合,存储多个学生对象,使用程序实现在控制台遍历该集合,HashSet集合存储自定义类型元素,要想实现元素的唯一,要求必须重写hashCode方法和equals方法
学生类:
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.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 StudentTest {
public static void main(String[] args) {
//创建HashSet集合对象
HashSet<Student> hs = new HashSet<Student>();
//创建学生对象
Student s1 = new Student("韩信", 30);
Student s2 = new Student("李白", 35);
Student s3 = new Student("露娜", 33);
Student s4 = new Student("露娜", 33);
//把学生添加到集合
hs.add(s1);
hs.add(s2);
hs.add(s3);
hs.add(s4);
//遍历集合(增强for)
for (Student s : hs) {
System.out.println(s.getName() + "," + s.getAge());
}
}
}