Java集合的继承体系:
Collection
从体系图中可以看出,Collection集合中有两大分类,List集和Set集,它们都继承了Collection接口。
Collection的底层是 Object[] 数组,可以保存任何类型元素,在保存基本数据类型时,先把基本类型转换为对应的包装类类型, 因为包装类是Object的子类,可以进行存储。
Collection集合常用方法:
- boolean add() : 添加元素
- void clear() : 清空集合
- boolean remove(): 删除某个元素
- boolean isEmpty() :判断集合中是否为空
- boolean contains() : 是否包含某个元素
- int size() :返回集合中元素个数
迭代器(Iterator):
迭代器是一种模式,它可以使序列类型的数据结构的遍历行为和被遍历的对象分离,我们无需关心该序列底层数据结构,只要拿到这个对象,使用迭代器就可以遍历这个对象的内部数据
(1) 创建迭代器对象
- Iterator it = 对象.iterator();
(2) Iterator方法
-
boolean hasNext() : 判断光标下一位是否还有元素,有就返回true,没有就返回false,生成迭代器的时候,光标不是指向第一个元素的,而是在最顶端,没有指向任何元素,光标不会自动复位,使用完之后,必须重新生成
-
next() : 把光标向下移动一位,并返回该位上的数据
-
remove():删除当前光标指向的元素
(3) 注意事项
迭代器一旦创建,如果集合中添加或者删除元素,迭代器必须重新生成,否则会报异常·java.util.ConcurrentModificationException
使用迭代器进行遍历时,如果需要删除元素,只能使用迭代器的remove方法,不能使用集合的remove方法
List
List集合存储的元素 有序,可重复。有序指得是存取顺序一致,并且有指定的下标可以表示数据的唯一性,所以可以存在重复数据。
List接口有两个实现类,ArrayList 和 LinkedList。
- ArrayList : 底层是数组,查询效率较高,添加和删除效率较低
- LinkedList : 底层是双向链表,查询效率较低,添加和删除效率较高
ArrayList 底层是索引数组,下标0开始,初始化容量为10 ,如果存满了,会进行扩容,容量为原始容量的1.5倍,非线程安全,效率高。
LIst 常用方法:
- add(E e) : 将指定元素添加到尾部
- add(int index,E e) : 将指定元素添加到指定位置,原位置内容统一向后移动
- set(int index,E e) : 更改指定位置上的元素值
- get(int index) : 获取指定索引上的元素
- remove(int index) : 删除指定索引上的元素
- remove(Object o) : 删除指定元素
LinkedList 常用方法:
- add(E e) : 添加到尾部
- push(E e) : 添加到头部
- addFirst(E e) : 添加到头部
- addLast(E e) : 添加到尾部
- offerFirst(E e) 添加到头部,成功返回true
- offerLast(E e) : 添加到尾部,成功返回true
- get(int index) : 返回指定下标对应的数据(链表没有下标,只是模拟下标,方便我们查询使用)
Set
Set集合元素特点 : 无序,唯一
有 TreeSet 和 HashSet 实现类,TreeSet底层是红黑树,HashSet底层是散列表
(1) TreeSet
底层是红黑数, 元素按顺序保存
- 数字 : 默认从小到大
- 字符串 : 按位比较,默认比较每位ASCII码
- 日期 : 默认比较自然日期 昨天-今天-明天
TreeSet treeSet = new TreeSet();
// 如果字符串有多个字符,先比较各自的第一位,谁小谁先输出
// 如果第一位相同,再比较第二位,以此类推
//所以先输出 101 ,后输出11
treeSet.add("11");
treeSet.add("101");
for (Object object : treeSet) {
System.out.print(object);
}
比较器
比较器有两种 : 元素自身比较器 和 比较器类
为什么字符串,Integer,Date可以排序?
- 因为都实现了Comparable接口
使用TreeSet在进行数据添加的时候,会自动调用该对象的compareTo()方法和集合内元素进行比较
如果我们想要存储自定义类型怎么办?
- 实现Comparable接口,重写compareTo方法
(1)自定义数据类型,实现Comparable接口,重写compareTo方法
class User implements Comparable{
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public User(int age) {
super();
this.age = age;
}
@Override
public String toString() {
return "User [age=" + age + "]";
}
@Override
public int compareTo(Object o) {
// this 是当前对象
// o 是集合内对象
// 返回值为0 表示相等,不添加
// 返回大于0 表示要添加的元素大,则放到后面
// 返回小于0 表示要添加的元素小,则放到前面
User user = (User) o;
// 升序
return this.age - user.age;
// 降序
// return this.age - user.age;
}
}
(2) 测试
public static void main(String[] args) {
TreeSet treeSet = new TreeSet();
//创建三个自定义类型对象
User user1 = new User(11);
User user2 = new User(13);
User user3 = new User(6);
//添加到TreeSet集合中
treeSet.add(user1);
treeSet.add(user2);
treeSet.add(user3);
for (Object object : treeSet) {
System.out.println(object);
}
}
结果:(根据重写的compareTo方法,对象按照年龄升序排列)
3
User [age=6]
User [age=11]
User [age=13]
treeSet添加的元素第二种排序方式:
使用 java.util.Comparator
- 单独创建比较器类,实现Comparator接口,重写compare()方法
- 传入匿名内部类
使用 Comparable 或 Comparator 进行比较的应用场景
- 如果添加的元素的类是我们写的,我们应该使用 Comparable , 因为对扩展开发,其他人还可以使用 Comparator 实现新的排序功能
- 如果添加的元素的类不是我们写的
-
1 该类有排序(实现了Comparable) 比如Integer,但是默认排序结果不是我们想要的,那么我们可以使用Comparator进行调整排序,因为优先级高
-
2 如果该类没有实现排序(没有实现Comparable), 这时候我们需要使用Comparator进行排序,因为我们不可能去改变人家类的源码
使用 Comparator 进行排序实例:
Integer 类实现了Comparable 接口,但默认排序方式是升序,我们可以使用 Comparator 进行降序排列。
public static void main(String[] args) {
//参数传入内部类
TreeSet treeSet = new TreeSet(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
Integer i1 = (Integer) o1;
Integer i2 = (Integer) o2;
return i2-i1;//降序
}
});
//添加元素
treeSet.add(1);
treeSet.add(12);
treeSet.add(11);
treeSet.add(3);
treeSet.add(5);
//遍历输出
for (Object object : treeSet) {
System.out.println(object);
}
}
向 ArrayList 中添加 Integer类型数据,并对其排序:
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add(2);
arrayList.add(22);
arrayList.add(12);
arrayList.add(5);
Collections.sort(arrayList);
System.out.println(arrayList);//[2, 5, 12, 22]
}
因为 Integer 实现了 Comparable ,默认升序排列
改为降序:
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add(2);
arrayList.add(22);
arrayList.add(12);
arrayList.add(5);
Collections.sort(arrayList,new Comparator() {
@Override
public int compare(Object o1, Object o2) {
Integer i1 = (Integer) o1;
Integer i2 = (Integer) o2;
return i2-i1;
}
});
System.out.println(arrayList);//[22, 12, 5, 2]
}
散列表:
(1)散列表是一种数据结构,不过java中屏蔽了,直接以封装的形式封装到了HashSet, HashMap和HashTable中,其中hashTable已经过时
(2)结构:
数组中保存链表(单向链表),并且链表节点内有四个属性
- key
- value:自身保存的数据
- next :下一个节点的地址
- hash
HashSet 和 HashMap
- HashSet 就是HashMap的封装,本质就是一个HashMap
- 默认初始化容量都是 16
- 封装之后HashSet把value值屏蔽了,只能操作key,所以在使用set添加的时候,只需要传入key即可
散列表需要使用hashCode()和equals()来表示对象的唯一性
添加过程:
- 使用添加的键值对中的key,调用key的hashCode方法,生成hash值,进行hash算法得到数组下标
- 判断该下标上是否有元素,如果没有,把该键值对保存到该数组中即可
- 如果该数组中有对象,则调用key的equals方法和数组中的元素进行比较,如果相等则key不添加,value值覆盖
- 如果不相等,就把该对象添加到已有元素的next属性,形成链表
- 如果添加的时候,就已经是链表了,则需要用key和链表中所有的元素key进行比较是否相等