掌握Collection接口下每个集合底层的数据结构,熟悉集合的常用方法,学会创建集合并对其进行操作
文章目录
集合概述
- 集合实际上是一个容器,一个载体,可以容纳其他类型的数据。
- 集合不能直接存储基本数据类型,也不能直接存储Java对象,集合当中存储的都是Java对象的内存地址(存储的是引用)。
list.add(100); //自动装箱 Integer
- 在Java中每一个不同的集合,底层会对应不同的数据结构,往不同的集合中存储元素,等于数据放到了不同的数据结构当中。数据存储的结构就是数据结构。不同的数据结构,数据存储方式不同。
- 所有的集合类和集合接口在java.until包下
Collection集合继承结构
结构继承图:
Collection接口
Collection接口的常用方法
以为Collection接口是公共的,所有集合都有这个接口中的方法,Collection接口的常用方法如下:
boolean add(Object e); 向集合中添加元素
int size(); 获取集合元素的个数
void clear(); 清空集合
boolean contains(Object o); 判断集合是否包含元素o
boolean remove(Object o); 删除集合中某个元素
boolean isEmpty(); 判断集合中元素个数是否为0
Object[ ] toArray();把集合转换成数组
import java.util.ArrayList;
import java.util.Collection;
public class Test01 {
public static void main(String[] args) {
//创建集合对象
Collection c = new ArrayList();
//向集合中添加元素
c.add(1100); //自动装箱 Integer x = new Integer();
c.add(true);
c.add(new Object());
//获取集合中元素个数
System.out.println("集合中元素个数:"+c.size()); //3
//删除集合中某个元素
c.remove(1100);
System.out.println("集合中元素个数:"+c.size()); //2
//清空集合
c.clear();
System.out.println("集合中元素个数:"+c.size()); //0
//判断集合中是否包含某个元素
c.add("Hello");
c.add("word");
c.add("lic");
System.out.println(c.contains("lic")); //true
//判断集合是否为空(集合中是否存在元素)
System.out.println(c.isEmpty()); //false
//集合转化成数组
Object[] objects = c.toArray();
for (Object o: objects
) {
System.out.print(o+" ");
}
}
}
运行结果如下:
常用方法中的contains方法和remove方法在使用时会调用equals方法。
以上源码可知,contains方法调用indexOf方法,而indexOf方法调用equals方法。所以集合在判断是否包含某个元素o,集合会拿着自己集合中每一个元素和o比较,如果equals方法返回为true,则表示包含元素o。
示例:自定义Date类,此时没有重写equals方法,看看会出现问题吗。
class Date{
private int year;
private int month;
private int day;
public Date(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
}
测试类:
import java.util.ArrayList;
import java.util.Collection;
public class Test02 {
public static void main(String[] args) {
//创建集合
Collection c = new ArrayList();
//创建Date对象 d1
Date d1 = new Date(2022, 8, 27);
//将d1添加到集合c
c.add(d1);
//判断集合中是否包含
Date d2 = new Date(2022, 8, 27);
System.out.println(c.contains(d2));
}
}
运行结果:
但是通过看代码,实际上集合中包含“2022年8月27日”这个数据,但结果说明不包含,这是出现了什么问题?
问题在于没有重写Date的equals方法,不重写equals方法,调用的是Object类的equals。比较对象的内存地址,这里应该比较内容,就需要重写Date类的equals方法。
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Date date = (Date) o;
return year == date.year && month == date.month && day == date.day;
}
运行结果:
说明放在集合中的元素是需要重写equals方法的!!
测试remove方法有没有调用equals方法。
import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;
public class Test02 {
public static void main(String[] args) {
//创建集合
Collection c = new ArrayList();
//添加a元素
Integer a = new Integer(100);
c.add(a);
//删除b元素
Integer b = new Integer(100);
c.remove(b);
//元素个数
System.out.println(c.size());
}
运行结果:
以上程序中删除集合的b元素,a也被删除了。说明remove方法调用equals方法。更加说明了存储在集合中的元素需要重写equals方法。
Collection接口的通用迭代
集合的遍历又叫集合的迭代,Java的集合框架中,Collection接口继承了Iterable接口,表示Collection集合元素都是可迭代的,集合Collection通过调用iterator方法获取迭代器对象,通过Iterator迭代器接口的hasNext和next方法完成集合元素的迭代。
示例:
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
/*
集合遍历、迭代专题
*/
public class Text03 {
public static void main(String[] args) {
Collection c = new ArrayList();
c.add(100);
c.add("hello");
c.add(new Object());
c.add(100);
//获取集合的迭代器对象
Iterator it = c.iterator();
/*
迭代器对象的两个方法
boolean hasNext 返回true表示还有元素可以迭代
Object next 让迭代器前进一位,并将指向的元素返回
*/
while (it.hasNext()){
//存进去什么类型,取出来还是什么类型
Object obj = it.next();
//输出转换成字符串,println调用toString类型
System.out.println(obj);
}
}
}
运行结果如下:
集合迭代过程中的删除
使用迭代器遍历集合过程中删除集合的元素,调用迭代器的remove方法删除元素
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
/*
集合遍历、迭代专题
*/
public class Text03 {
public static void main(String[] args) {
//创建集合
Collection c2 = new HashSet();
//加入元素
c2.add(100);
c2.add(100);
c2.add("lic");
c2.add(200);
c2.add(400);
c2.add("li");
//生成迭代器
Iterator it2 = c2.iterator();
while(it2.hasNext()){
Object obj2 = it2.next();
//使用迭代器的remove方法删除元素
it2.remove();
System.out.println(obj2);
}
System.out.println(c2.size());
}
}
List接口
- 1.List集合特点:有序可重复。 元素有下标;从0开始,以1递增。存储一个1可以再存储一个1
- 2.List集合是Collection接口的子接口,有特有的方法。
- 在Java中,List接口是一个有序的集合,它允许我们按顺序存储和访问元素。
- 来看一下List集合的特有的方法。
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/*
1.List集合特点:有序可重复。 元素有下标;从0开始,以1递增。存储一个1可以再存储一个1
2.List集合是Collection接口的子接口,有特有的方法
*/
public class Test01 {
public static void main(String[] args) {
//创建List接口的集合
List list = new ArrayList();
//添加元素
list.add("A");
list.add("A");
list.add("B");
list.add("C");
list.add("D");
list.add(1,"lic");
//生成迭代器
Iterator it = list.iterator();
//遍历
while ((it.hasNext())){
System.out.println(it.next());
}
//根据下标获取元素
System.out.println(list.get(0));
//有下标,通过下标遍历集合的元素
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
//获取指定对象第一次出现的索引
System.out.println(list.indexOf("A"));
//获取指定对象最后一次出现的索引
System.out.println(list.lastIndexOf("A"));
//删除指定下标位置的元素
list.remove(1);
System.out.println(list.size());
//修改指定位置的元素
System.out.println(list.set(1, "lng"));
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
List集合之ArrayList
- 默认初始化容量是10
- 集合底层是一个Object[ ] 数组
- 底层是数组的优点:检索效率比较高;缺点:随意增删元素效率低
- 构造方法: new ArrayList(); (无参构造)。 new ArrayList(int a);(有参构造)ArrayList(Collection c); (构造一个包含指定collection的元素的列表,元素按照该collection的迭代器返回排列顺序)
- ArrayList集合的扩容:原容量的1.5倍
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
public class Test02 {
public static void main(String[] args) {
//默认初始化为0
List list1 = new ArrayList();
//指定初始化容量100
List list2 = new ArrayList(100);
//创建HashSet集合
Collection c = new HashSet();
//添加元素
c.add(1);
c.add(2);
c.add(3);
//HashSet集合转化为List集合
List list3 = new ArrayList(c);
for (int i = 0; i < list3.size(); i++) {
System.out.println(list3.get(i));
}
}
}
List集合之LinkedList
LinkedList底层数据结构的链表:单向链表、双向链表、循环链表。
链表最基本的单元是结点,在单向链表中,每一个结点由两部分组成:存储的数据data、指向下一结点的内存地址next。并且每个结点在空间存储上内存地址不连续,和数组完全不同。
在双向链表中,有三部分组成:存储的数据data、指向上一结点的内存地址、指向下一结点的内存地址。
每个结点在空间存储上内存地址不连续,无法通过数学表达式计算要查找的元素的内存地址,所以只能从头节点开始,一个一个挨着查找,所以检索效率低。但是新增或删减元素时,就不需要涉及大量元素位移问题,所以增删效率高。
综上:
链表的优点:随机增删元素效率高。
链表的缺点:检索效率较低。
List集合之Vector
Vector底层也是数组,初始化容量是10,扩容后是原容量的2倍,Vector中所有的方法都是线程同步的,带有synchronized关键字,是线程安全的。效率低,使用少。
将线程不安全的ArrayList集合转换成线程安全:
Collections.synchronizedList(ArrayList);
Set接口
Set集合存储元素的特点:无序不可重复。无序指元素没有下标,不可重复就是存一个元素后,就不能存相同的元素了。
Set集合之HashSet
-
HashSet底层就是HashMap
-
向HashSet中添加元素,实际就是把该元素作为键添加到底层的HashMap
-
HashSet就是HashMap键的集合
HashSet hashSet = new HashSet();
hashSet.add("a");
hashSet.add("a");
hashSet.add("b");
hashSet.add("ff");
hashSet.add("ee");
for (Object s:hashSet
) {
System.out.println(s);
}
运行结果:
Set集合之TreeSet
TreeSet集合实现了SortSet接口。特点是:没有下标,不可重复,但是存进去的元素会自动按照大小顺序排序,称为可排序集合(无法对自定义类型排序,因为没有说明自定义类型的比较规则)。
- TreeSet底层是TreeMap
- 在创建TreeSet时,可以指定Comparator比较器,对集合中的元素进行比较
import java.util.Comparator;
import java.util.TreeSet;
public class Test06 {
public static void main(String[] args) {
TreeSet<String> treeSet = new TreeSet<>(Comparator.reverseOrder());
treeSet.add("ad");
treeSet.add("ac");
treeSet.add("bc");
//调用比较器 降序排序
System.out.println(treeSet);
}
}
- 没有指定Comparator比较器,就要求元素的类实现Comparable接口
import java.util.Iterator;
import java.util.TreeSet;
//TreeSet里编写Comparable接口
//如果年龄相同,比较名字
public class Test08 {
public static void main(String[] args) {
TreeSet<user> users = new TreeSet<>();
users.add(new user(12,"ued"));
users.add(new user(12,"lic"));
users.add(new user(15,"drc"));
users.add(new user(13,"wsc"));
Iterator<user> iterator = users.iterator();
while (iterator.hasNext()){
user next = iterator.next();
System.out.println(next);
}
}
}
class user implements Comparable<user>{
int age;
String name;
public user(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "user{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
@Override
public int compareTo(user o) {
if(this.age==o.age){
return this.name.compareTo(o.name);
}else{
return this.age-o.age;
}
}
}
结果如下:
- 如果不调用,默认是升序
//不调用 默认升序
TreeSet<String> treeSet1 = new TreeSet<>();
treeSet1.addAll(treeSet);
System.out.println(treeSet1);
结果如下:
泛型
泛型就是把数据类型作为参数传递,在集合框架使用频繁,泛型的好处是在编译时可以进行数据类型的检查。
- 没有使用泛型,不明确集合存储元素是什么类型,可能会进行强制转型
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Test07 {
public static void main(String[] args) {
Collection list = new ArrayList();
//添加元素
list.add("aaa");
list.add("bbb");
list.add("ccc");
//遍历
Iterator iterator = list.iterator();
while (iterator.hasNext()){
//没有使用泛型,返回Object类型
Object s = iterator.next();
//使用String类的方法需要 向下转型
String str = (String) s;
System.out.println(str.substring(1));
}
}
}
- 使用泛型后,在编译阶段起作用,明确元素类型,不用强转类型了
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Test07 {
public static void main(String[] args) {
//定义集合使用泛型,list只能存储String类型的元素
Collection<String> list = new ArrayList<>();
//添加元素
list.add("aaa");
list.add("bbb");
list.add("ccc");
//遍历
Iterator<String> iterator = list.iterator();
//使用泛型后,iterator返回String类型,而不是Object类型
while (iterator.hasNext()){
String s = iterator.next();
System.out.println(s.substring(1));
}
}
}
如何自定义泛型
自定义泛型时,<>里面的内容是标识符,任意定义合法的标识符。比如下面Test05使用的泛型类型是,那么Method方法的参数类型也必须是T
import java.util.*;
//自定义泛型
public class Test05 <T> {
public void Method(T o){
System.out.println(o);
}
public static void main(String[] args) {
Test05<Integer> a = new Test05<>();
a.Method(12);
Test05<String> b = new Test05<>();
b.Method("aa");
}
}
Collections工具类
Collection是一个集合接口,Collections是一个集合工具类,升序排序、降序排序、二分法查找、非线程安全集合转换成线程安全集合,但是Collections传进去的参数只能是List。
import java.util.*;
//Collection是一个集合接口 Collections是一个集合工具类
public class collections {
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
//Set集合之后就是线程安全的
Collections.synchronizedSet(set);
//添加元素
set.add(21);
set.add(5);
set.add(15);
set.add(8);
//通过构造方法 Set集合转化为List集合
List<Integer> list = new ArrayList<>(set);
//升序排序
Collections.sort(list);
//输出
for (Integer s:list
) {
System.out.println(s);
}
//二分法查找
System.out.println("5所在的下标:"+Collections.binarySearch(list, 8));
//降序排序 反转
Collections.reverse(list);
for (Integer in:list
) {
System.out.println(in);
}
}
}