文章目录
1集合
集合是可变的容器,关于容器的具体知识,转移至之前的容器相关文章。
在学习集合之前首先理解数组和集合的区别
- 相同点
都是容器,可以存储多个数据 - 不同点
- 数组的长度是不可变的,集合的长度是可变的
- 数组可以存基本数据类型和引用数据类型
- 集合只能存引用数据类型,如果要存基本数据类型,需要存对应的包装类
集合主要是目前主要理解包括单列集合Collection和双列集合Map集合。
1.1Collection集合
1.11 Collection概述
Collection是单列集合的顶层接口,它表示一组对象,这些对象也被称为Collection的元素
- 创建Collection集合对象的实现方式
- 多态的方式,Collection指向子类如,new ArrayList() 或new LinkList等
- Collection集合常用方法
方法名 | 说明 |
---|---|
boolean add(E e) | 添加元素 |
boolean remove(Object o) | 从集合中移除指定的元素 |
boolean removeIf(Object o) | 根据条件进行移除 |
void clear() | 清空集合中的元素 |
boolean contains(Object o) | 判断集合中是否存在指定的元素 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中元素的个数 |
1.2迭代器概述
前面讲解循环章节的时候,提到过迭代器是集合专用的遍历方式。
- 迭代器的原理
- 迭代器主要方法是hasNext()方法判断当前位置是否有元素,next() 方法取出当前元素,指针向下移动一个位置来配合来使用。创建的迭代器对象是临时的,复制之前集合的。因此迭代器用于遍历集合,而不是删除。增强for的底层也是基于迭代器来实现的,因此也可以使用增强for来遍历集合。
- 迭代器使用条件
- 使用的类必须实现Iterable接口或接口继承Iterable其本身和子类才能使用迭代器进行遍历。
通过API文档可以发现,单列集合Collection继承了Iterable,因此单列集合都可以直接使用迭代器和增强for。双列集合Map没有继承Iterable接口,因此不能直接使用迭代器。
- 使用的类必须实现Iterable接口或接口继承Iterable其本身和子类才能使用迭代器进行遍历。
1.3 List集合
Collection集合下方的子接口包括List集合和Set集合。
1.31List集合概述
- List集合的特点
- List集合是有序集合,这里指的是存取顺序
- List集合的元素允许重复
- List集合有索引,可以通过索引操作List里的元素
方法名 | 描述 |
---|---|
void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
E get(int index) | 返回指定索引处的元素 |
1.32List集合的实现类ArrayList和LinkList
ArrayList底层数据结构是数组,LinkList底层数据结构是双向链表。
- ArrayList和LinkList的区别,通过二者的数据结构不难发现区别
- ArrayList查询速度快,增删速度慢
- LinkList查询速度慢,增删速度块
方法名 | 说明 |
---|---|
public void addFirst(E e) | 在该列表开头插入指定的元素 |
public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
public E getFirst() | 返回此列表中的第一个元素 |
public E getLast() | 返回此列表中的最后一个元素 |
public E removeFirst() | 从此列表中删除并返回第一个元素 |
public E removeLast() | 从此列表中删除并返回最后一个元素 |
1.4Set集合
1.41Set集合概述
- 不能存储重复元素
- 没有索引,不能使用普通for遍历
- 存取顺序可能不一致
- 底层数据结构是红黑树(JDK1.8以后)
1.42TreeSet集合
TreeSet是Set集合的实现类,因此Set有的特征,TreeSet也有,可以知道TreeSet集合没有索引,元素不能重复,存取顺序不一致。TreeSet根据指定的比较器进行排序
1.42TreeSet集合的两种比较器
自然排序和比较器排序比较的返回值都是Int,返回结果为负数当前对象排在左边,结果为0则两个元素为重复元素,舍弃。结果为正数,当前对象排在右边。
-
1.自然排序Comparable
- 创建TreeSet空参对象
- 自定义的类实现Comparable接口,重写CompareTo(T o)方法
- 重写接口中的compareTo方法要按照主要条件和次要条件进行排序
-
代码实现
学生类
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; //次要判断条件: 年龄相同时,按照姓名的字母顺序排序 result = result == 0 ? this.name.compareTo(o.getName()) : result; return result; } }
测试类
public class MyTreeSet2 { 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); } } }
-
2.比较器排序Compartor
- 用TreeSet带参构造创建对象
- 比较器排序,就是让TreeSet集合的构造方法接收Comparator的实现类对象(匿名内部类),重写Compare(T 01,T 02)方法
- 重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
-
代码实现
老师类
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 "Teacher{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
测试类
public class MyTreeSet4 {
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 t1 = new Teacher("zhangsan",23);
Teacher t2 = new Teacher("lisi",22);
Teacher t3 = new Teacher("wangwu",24);
Teacher t4 = new Teacher("zhaoliu",24);
//把老师添加到集合
ts.add(t1);
ts.add(t2);
ts.add(t3);
ts.add(t4);
//遍历集合
for (Teacher teacher : ts) {
System.out.println(teacher);
}
}
}
- TreeSet集合的两种比较器总结
- 自然排序:自定义实现Comparable接口,重写compareTo方法,根据返回值进行排序
- 比较器排序:创建TreeSet对象的时候传递Comparator的实现类对象,重写compare方法,根据返回值进行排序
- 在使用的时候,默认使用自然排序,当自然排序不满足现在需求时,就必须使用比较器排序、
- 两个方式中关于返回值的规则
- 如果返回值为负数,表示当前存入的元素是较小值,存左边
- 如果返回值为0,表示当前存入的元素跟集合中的元素重复,不存进集合
- 如果返回值为正数,表示当前存入的元素是较大值,存右边
1.43HashSet集合
1.431HashSet集合概述
- 底层数据结构是哈希表
- 存取无序
- 不可以存储重复元素
- 没有索引,不能使用普通for循环遍历
1.432哈希值
-
哈希值简介
是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
-
如何获取哈希值
Object类中的public int hashCode():返回对象的哈希码值 -
哈希值的特点
- 同一个对象多次调用hashCode()方法返回的哈希值是相同的
- 默认情况下,不同对象的哈希值是不同的。而重写hashCode()方法,可以实现让不同对象的哈希值相同
- 我们知道equals比较两个对象的地址是否相同,因此对于引用类型,equals相同,hashCode一定相同。而HashCode相同,equals不一定相同。
哈希表结构
创建一个默认长度为16,默认加载因子0.75的数组,这是默认的哈希表。当哈希值相同的不同元素,会采用头插法挂在对应数组下方形成链表。jdk1.8及以后链表超过8,采用红黑树。
总结:
-
JDK1.8以前
数组 + 链表
-
JDK1.8以后
- 节点个数少于等于8个
数组 + 链表 - 节点个数多于8个
数组 + 红黑树
总结
HashSet集合存储自定义类型元素,要想实现元素的唯一,要求必须重写hashCode方法和equals方法
- 节点个数少于等于8个
2泛型
- 泛型的介绍
- 泛型是JDK5中引入的特性,它提供了编译时类型安全检查机制
- 泛型的好处
- 1.把运行时期的问题提前到了编译时期
- 2.避免了强制类型转换
- 泛型的定义格式
- <类型>: 指定一种类型的格式.尖括号里面可以任意书写,一般只写一个字母.例如:
- <类型1,类型2…>: 指定多种类型的格式,多种类型之间用逗号隔开.例如: <E,T> <K,V>
2.1泛型应用
泛型用于主要类,接口,方法 ,分别称为泛型类、泛型接口和泛型方法。
2.11泛型类
- 定义格式:
修饰符 <类型> 返回值类型 方法名(类型 变量名) { }
- 示例:
public class Generic<T> {
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
2.12泛型方法
-
定义格式
修饰符 <类型> 返回值类型 方法名(类型 变量名) { }
注意:在方法返回类型前面加上泛型声明,告诉方法中的参数这是一个方法,而不是一个类名叫T.如果在接口中或者方法上面已近声明了,则可以不用在返回值前处声明。
- 示例
- 定义格式
public class Generic {
public <T> void show(T t) {
System.out.println(t);
}
}
2.13泛型接口
-
定义格式
修饰符 interface 接口名<类型> { }
-
示例:
-
泛型接口
public interface Generic<T> { void show(T t); }
-
-
泛型接口实现类1
定义实现类时,定义和接口相同泛型,创建实现类对象时明确泛型的具体类型
public class GenericImpl1<T> implements Generic<T> { @Override public void show(T t) { System.out.println(t); } }
-
泛型接口实现类2
定义实现类时,直接明确泛型的具体类型,实现的接口的类就变成一个普通即可
public class GenericImpl2 implements Generic<Integer>{
@Override
public void show(Integer t) {
System.out.println(t);
}
}
2.2类型通配符
类型的作用,不用声明这是一个泛型,直接用。其二,类型通配符可以表示范围。
-
类型通配符上限: <? extends 类型>
- ArrayListList <? extends Number>: 它表示的类型是Number或者其子类型
-
类型通配符下限: <? super 类型>
- ArrayListList <? super Number>: 它表示的类型是Number或者其父类型
-
泛型通配符的使用
public class GenericDemo4 { public static void main(String[] args) { ArrayList<Integer> list1 = new ArrayList<>(); ArrayList<String> list2 = new ArrayList<>(); ArrayList<Number> list3 = new ArrayList<>(); ArrayList<Object> list4 = new ArrayList<>(); method(list1); method(list2); method(list3); method(list4); getElement1(list1); getElement1(list2);//报错 getElement1(list3); getElement1(list4);//报错 getElement2(list1);//报错 getElement2(list2);//报错 getElement2(list3); getElement2(list4); } // 泛型通配符: 此时的泛型?,可以是任意类型 public static void method(ArrayList<?> list){} // 泛型的上限: 此时的泛型?,必须是Number类型或者Number类型的子类 public static void getElement1(ArrayList<? extends Number> list){} // 泛型的下限: 此时的泛型?,必须是Number类型或者Number类型的父类 public static void getElement2(ArrayList<? super Number> list){} }