一、集合
集合分为两类:一类是单列集合元素是一个一个的,另一类是双列集合元素是一对一对的。
- Collection代表单列集合,每个元素(数据)只包含一个值。
- Map代表双列集合,每个元素包含两个值(键值对)。
二、Collection单列集合
Collection集合特点
List系列集合:添加的元素是有序、可重复、有索引。
- ArrayList、LinekdList :有序、可重复、有索引。
Set系列集合:添加的元素是无序、不重复、无索引。
-
HashSet: 无序、不重复、无索引;
-
LinkedHashSet: 有序、不重复、无索引。
-
TreeSet:按照大小默认升序排序、不重复、无索引。
- Collection规定的方法(功能)是全部单列集合都会继承的。
Collection的常见方法:
方法名 | 说明 |
---|---|
public boolean add(E e) | 把给定的对象添加到当前集合中 |
public void clear() | 清空集合中所有的元素 |
public boolean remove(E e) | 把给定的对象在当前集合中删除 |
public boolean contains(Object obj) | 判断当前集合中是否包含给定的对象 |
public boolean isEmpty() | 判断当前集合是否为空 |
public int size() | 返回集合中元素的个数。 |
public Object[] toArray() | 把集合中的元素,存储到数组中 |
//8.把集合转换为指定类型的数组,可以使用下面的代码
String[] array1 = c.toArray(new String[c.size()]);
System.out.println(Arrays.toString(array1)); //[java1,java2, java2, java3]
三、集合的遍历方式
1.迭代器
概述
- 迭代器是用来遍历集合的专用方式(数组没有迭代器),在Java中迭代器的代表是Iterator。
Collection集合获取迭代器的方法
方法名称 | 说明 |
---|---|
Iterator iterator() | 返回集合中的迭代器对象,该迭代器对象默认指向当前集合的第一个元素 |
Iterator迭代器中的常用方法
方法名称 | 说明 |
---|---|
boolean hasNext() | 询问当前位置是否有元素存在,存在返回true ,不存在返回false |
E next() | 获取当前位置的元素,并同时将迭代器对象指向下一个元素处。 |
Collection<String> c = new ArrayList<>();
Iterator<String> it = c.iterator();
while(it.hasNext()){
String e = it.next();
System.out.println(s);
}
1.当调用iterator()方法获取迭代器时,当前指向第一个元素
2.hasNext()方法则判断这个位置是否有元素,如果有则返回true,进入循环
3.调用next()方法获取元素,并将当月元素指向下一个位置,
4.等下次循环时,则获取下一个元素,依此内推
2.增强for循环
- 增强for可以用来遍历集合或者数组。
- 增强for遍历集合,本质就是迭代器遍历集合的简化写法。
//格式
for(元素的数据类型 变量名 : 数组或者集合) {
//在此处使用变量即可,该变量就是元素
}
//
Collection<String> c = new ArrayList<>();
for(String s: c){
System.out.println(s);
}
3.forEach遍历集合
常用方法
方法名称 | 说明 |
---|---|
default void forEach(Consumer<? super T> action) | 结合lambda遍历集合 |
Collection<String> lists = new ArrayList<>();
lists.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.*out*.println(s);
}
});
// 简化
lists.forEach(s -> {
System.*out*.println(s);
});
// 继续简化
lists.forEach(s -> System.out.println(s));*
四、List集合
1.List的方法
- List集合支持索引,Collection的功能List也都继承了。
方法名称 | 说明 |
---|---|
void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
E get(int index) | 返回指定索引处的元素 |
List遍历
- 普通for循环(List有索引)
- 迭代器
- 增强for
- Lambda表达式
2.ArrayList
ArrayList底层的原理
ArrayList集合底层是基于数组结构实现的,也就是说当你往集合容器中存储元素时,底层本质上是往数组中存储元素。
- 查询速度快(根据索引查询数据快):查询数据通过地址值和索引定位,查询任意数据耗时相同。
- 删除效率低:可能需要把后面很多的数据进行前移。
- 添加效率极低:可能需要把后面很多的数据后移,再添加元素;或者也可能需要进行数组的扩容。
原理
①利用无参构造器创建的集合,会在底层创建一个默认长度为0的数组
②添加第一个元素时,底层会创建一个新的长度为10的数组
③存满时,会扩容1.5倍
④如果一次添加多个元素,1.5倍还放不下,则新创建数组的长度以实际为准
ArrayList集合适合的应用场景
ArrayList适合:根据索引查询数据比如根据随机索引取数据(高效)!或者数据量不是很大时!
ArrayList不适合:数据量大的同时又要频繁的进行增删操作!
3. LinkedList
LinkedList底层原理
基于双链表实现的。
特点:查询慢,增删相对较快,但对首尾元素进行增删改查的速度是极快的。
链表中的结点是独立的对象,在内存中是不连续的,每个结点包含数据值和下一个结点的地址。
方法名称 | 说明 |
---|---|
public void addFirst(E e) | 在该列表开头插入指定的元素 |
public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
public E getFirst() | 返回此列表中的第一个元素 |
public E getLast() | 返回此列表中的最后一个元素 |
public E removeFirst() | 从此列表中删除并返回第一个元素 |
public E removeLast() | 从此列表中删除并返回最后一个元素 |
LinkedList的应用场景:可设计队列
队列的特点:先进先出,后进后出
五、Set集合
1.Set集合
Set要用到的常用方法,基本上就是Collection提供的
Set系列集合特点:无序:添加数据的顺序和获取出的数据顺序不一致; 不重复; 无索引
HashSet : 无序、不重复、无索引。
LinkedHashSet:有序、不重复、无索引。
TreeSet:排序、不重复、无索引。
2.哈希值
就是一个int类型的数值,ava中每个对象都有一个哈希值。
Java中的所有对象,都可以调用Obejct类提供的hashCode方法,返回该对象自己的哈希值。
public int hashCode():返回对象的哈希码值。
对象哈希值的特点
同一个对象多次调用hashCode()方法返回的哈希值是相同的。
不同的对象,它们的哈希值一般不相同,但也有可能会相同(哈希碰撞)。
哈希表是一种增删改查数据性能都较好的结构
3.HashSet集合
HashSet集合底层原理
HashSet集合底层是基于哈希表实现的,哈希表根据JDK版本的不同,也是有点区别的
- JDK8以前:哈希表 = 数组+链表
① 创建一个默认长度16,默认加载因为0.75的数组,数组名table
② 根据元素的哈希值跟数组的长度计算出应存入的位置
③ 判断当前位置是否为null,如果是null直接存入,如果位置不为null,表示有元素,则调用equals方法比较属性值,如果一样,则不存,如果不一样,则存入数组。
④ 当数组存满到16*0.75=12时,就自动扩容,每次扩容原先的两倍
JDK 8之前,新元素存入数组,占老元素位置,老元素挂下面
JDK 8开始之后,新元素直接挂在老元素下面
HashSet集合中存储元素时,底层调用了元素的两个方法:一个是hashCode方法获取元素hashCode值(哈希值);另一个是调用了元素的equals方法,用来比较新添加的元素和集合中已有的元素是否相同。
- 只有新添加元素的hashCode值和集合中以后元素的hashCode值相同、新添加的元素调用equals方法和集合中已有元素比较结果为true, 才认为元素重复。
- 如果hashCode值相同,equals比较不同,则以链表的形式连接在数组的同一个索引为位置
- JDK8以后:哈希表 = 数组+链表+红黑树
JDK8开始后,为了提高性能,当链表的长度超过8时,就会把链表转换为红黑树
HashSet去重原理
HashSet集合默认不能对内容一样的两个不同对象去重复!
比如内容一样的两个学生对象存入到HashSet集合中去, HashSet集合是不能去重复的!
要想保证在HashSet集合中没有重复元素,需要重写元素类的hashCode和equals方法。
4.LinkedHashSet底层原理
- LinkedHashSet类是HashSet的子类
底层原理
LinkedHashSet底层采用的是也是哈希表(数组,链表,红黑树)结构,只不过额外新增了一个双向链表来维护元素的存取顺序。
特点
有序、不重复、无索引
5.TreeSet集合
特点:不重复、无索引、可排序(默认升序排序 ,按照元素的大小,由小到大排序)
底层是基于红黑树实现的排序。
注意
对于数值类型:Integer , Double,默认按照数值本身的大小进行升序排序。
对于字符串类型:默认按照首字符的编号升序排序。
对于自定义类型如Student对象,TreeSet默认是无法直接排序的。
自定义排序规则
TreeSet集合存储自定义类型的对象时,必须指定排序规则,支持如下两种方式来指定比较规则。
方式一
让自定义的类(如学生类)实现Comparable接口,重写里面的compareTo方法来指定比较规则。
public class Student implements Comparable<Student>{
private String name;
private int age;
private double height;
//无参数构造方法
public Student(){}
//全参数构造方法
public Student(String name, int age, double height){
this.name=name;
this.age=age;
this.height=height;
}
//...get、set、toString()方法自己补上..
//按照年龄进行比较,只需要在方法中让this.age和o.age相减就可以。
/*
原理:
在往TreeSet集合中添加元素时,add方法底层会调用compareTo方法,根据该方法的
结果是正数、负数、还是零,决定元素放在后面、前面还是不存。
*/
@Override
public int compareTo(Student o) {
//this:表示将要添加进去的Student对象
//o: 表示集合中已有的Student对象
return this.age-o.age;
}
}
方式二
通过调用TreeSet集合有参数构造器,可以设置Comparator对象(比较器对象,用于指定比较规则。)
public TreeSet(Comparator<? super E> comparator)
Set<Student> students = new TreeSet<>(new Comparator<Student>{
@Override
public int compare(Student o1, Student o2){
//需求:按照学生的身高排序
return Double.compare(o1,o2);
}
});
两种方式中,关于返回值的规则:
- 如果认为第一个元素 > 第二个元素 返回正整数即可。
- 如果认为第一个元素 < 第二个元素返回负整数即可。
- 如果认为第一个元素 = 第二个元素返回0即可,此时Treeset集合只会保留一个元素,认为两者重复。
注意:如果类本身有实现Comparable接口,TreeSet集合同时也自带比较器,默认使用集合自带的比较器排序。
6.总结
1、如果希望记住元素的添加顺序,需要存储重复的元素,又要频繁的根据索引查询数据?
用ArrayList集合(有序、可重复、有索引),底层基于数组的。(常用)
2、如果希望记住元素的添加顺序,且增删首尾数据的情况较多?
用LinkedList集合(有序、可重复、有索引),底层基于双链表实现的。
3.、如果不在意元素顺序,也没有重复元素需要存储,只希望增删改查都快?
用HashSet集合(无序,不重复,无索引),底层基于哈希表实现的。 (常用)
4、如果希望记住元素的添加顺序,也没有重复元素需要存储,且希望增删改查都快?
用LinkedHashSet集合(有序,不重复,无索引), 底层基于哈希表和双链表。
5、如果要对元素进行排序,也没有重复元素需要存储?且希望增删改查都快?
用TreeSet集合,基于红黑树实现。
6.Collection的其他操作
可变参数
- 可变参数是一种特殊的形式参数,定义在方法、构造器的形参列表处,它可以让方法接收多个同类型的实际参数。
- 可变参数在方法内部,本质上是一个数组
public class ParamTest{
public static void main(String[] args){
//不传递参数,下面的nums长度则为0, 打印元素是[]
test();
//传递3个参数,下面的nums长度为3,打印元素是[10, 20, 30]
test(10,20,30);
//传递一个数组,下面数组长度为4,打印元素是[10,20,30,40]
int[] arr = new int[]{10,20,30,40}
test(arr);
}
public static void test(int...nums){
//可变参数在方法内部,本质上是一个数组
System.out.println(nums.length);
System.out.println(Arrays.toString(nums));
System.out.println("----------------");
}
}
- 一个形参列表中,只能有一个可变参数;否则会报错
- 一个形参列表中如果多个参数,可变参数需要写在最后;否则会报错
Collections工具类
Collections并不是集合,它比Collection多了一个s,一般后缀为s的类很多都是工具类。这里的Collections是用来操作Collection的工具类。
待续未完