常见的是ArrayList、LinkedList
特点
①有序:存储和取出的元素顺序一致
②有索引:可以通过索引操作元素
③可重复:存储的元素可以重复
List集合特有方法
List集合因为支持索引,所以多了很多索引操作的独特API,其他Collection的功能List也都继承了。
常见方法如下
方法名称 | 说明 |
---|---|
void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
E get(int index) | 返回指定索引处的元素 |
(这些方法没什么好说的,还是直接调用就行)
List的实现类的底层原理
1)ArrayList底层是基于数组实现的,根据查询元素快,增删相对慢
2)LinkedList底层基于双链表实现的,查询元素慢,增删首尾元素是非常快的
List集合的遍历方式
①迭代器
②增强for循环
③Lambda表达式
④for循环(因为List集合存在索引)
ArrayList集合底层原理(简略了解一下就行)
LinkedList底层数据结构是双链表,查询慢,首尾操作的速度是极快的,所以它有许多首尾操作的特有API
LinkedList集合的常见特有功能
方法名称 | 功能 |
---|---|
public void addFirst(E e) | 在该列表开头插入指定的元素 |
public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
public E getFirst() | 返回此列表中的第一个元素 |
public E getLast() | 返回此列表中的最后一个元素 |
public E removeFirst() | 从此列表中删除并返回第一个元素 |
public E removeLast() | 从此列表中删除并返回最后一个元素 |
这些特有功能只有LinkedList集合能用,所以要用这些功能有两种方式
① LinkedList 集合名=new LinkedList<>();
②用多态时,后面操作强转一下就行了
代码展示
①
LinkedList<String> lists=new LinkedList<>();
②
List<String> lists=new LinkedList<>();
LinkedList linkedLists= (LinkedList) lists;
LinkedList可以完成栈结构和队列结构的
栈结构的特点为先进后出,后进先出。 所以只要一直在开头加元素,再一直去掉第一个元素就可以了
从专业角度讲,我们一般用push替代addFirst,pop替代 removeFirst,效果是一样的
代码如下
LinkedList<String> lists=new LinkedList<>();
//压栈,入栈
lists.push("第一颗子弹");
lists.push("第二颗子弹");
lists.push("第三颗子弹");
lists.push("第四颗子弹");
lists.push("第五颗子弹");
//弹栈,出栈
System.out.println(lists.pop()); // 第五颗子弹
System.out.println(lists.pop()); // 第四颗子弹
System.out.println(lists.pop()); // 第三颗子弹
System.out.println(lists.pop()); // 第二颗子弹
System.out.println(lists.pop()); // 第一颗子弹
队列结构的特点为先进先出,后进后出。 所以只要一直在最后加元素,再一直去掉第一个元素就可以了
代码如下
LinkedList<String> lists=new LinkedList<>();
//入队
lists.addLast("1号");
lists.addLast("2号");
lists.addLast("3号");
lists.addLast("4号");
lists.addLast("5号");
//出队
System.out.println(lists.removeFirst()); // 1号
System.out.println(lists.removeFirst()); // 2号
System.out.println(lists.removeFirst()); // 3号
System.out.println(lists.removeFirst()); // 4号
System.out.println(lists.removeFirst()); // 5号
当我们从集合中找出某个元素并删除的时候可能出现一种并发修改异常问题。
哪些遍历存在问题?
① 迭代器遍历集合且直接用集合删除元素的时候可能出现。
我前面说过了迭代器的遍历方式,我直接粘贴过来
方法名称 | 说明 |
---|---|
boolean hasNext() ; | 询问当前位置是否有元素存在,存在返回true,不存在返回false |
E next(); | 获取当前位置的元素,并同时将迭代器对象移向下一个位置,注意防止取出越界 |
这两个方法的配合使用代码如下
Iterator<String> it=Lists.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
我们如果在while中删除元素,即进行下面的操作
Iterator<String> it=lists.iterator();
while (it.hasNext()){
lists.remove(it.next());
}
it.next() 这一步是为了获取当前位置的元素,并同时将迭代器对象移向下一个位置,但我们删除元素后就相当于向下移动了两个位置,就会出现越界异常
②增强for循环遍历集合且直接用集合删除元素的时候可能出现。
因为增强for循环其内部原理是一个Iterator迭代器,遍历集合相当于是迭代器的简化形式,当然不能了,而且没有解决办法。
哪种遍历且删除元素不出问题
①迭代器遍历集合但是用迭代器自己的删除方法操作可以解决。(最好加个判断条件,否则会出现IllegalStateException异常,该异常表示,当前对客户端的响应已经结束,不能在响应已经结束(或说消亡)后再向客户端(实际上是缓冲区)输出任何内容。)(了解一下就行了)
while (it.hasNext()){
it.remove();
}
② 使用for循环遍历并删除元素不会存在这个问题。(就是用普通的for循环遍历)
概述
好处
public class Demo<T>{}
作用
编译阶段可以指定数据类型,类似于集合的作用。
原理
把出现泛型变量的地方全部替换成传输的真实数据类型。
概述
public static<T> void sum(){}
作用
方法中可以使用泛型接收一切实际类型的参数,方法更具备通用性。
原理(核心思想)
把出现泛型变量的地方全部替换成传输的真实数据类型。
概述
public interface Demo<T>{}
作用
泛型接口可以让实现类选择当前功能需要操作的数据类型
原理
实现类可以在实现接口的时候传入自己操作的数据类型,这样重写的方法都将是针对于该类型的操作。
泛型的上下限
特点
Set集合实现类特点
注意:Set集合的功能基本上与Collection的API一致
底层原理
哈希表
组成
在了解哈希表之前需要先理解哈希值的概念
哈希值: JDK根据对象的地址,按照某种规则算出来的int类型的数值。
我们可以通过Object类的一个API得到对象的哈希值
方法名称 | 作用 |
---|---|
public int hashCode(); | 返回对象的哈希值 |
对象的哈希值特点
1)同一个对象多次调用hashCode()方法返回的哈希值是相同的。
2)默认情况下,不同对象的哈希值是不同的。
Set集合底层用哈希表的详细流程(包含无序和去重复的原因)
①创建一个默认长度16,默认加载因为0.75的数组,数组名table
②根据元素的哈希值跟数组的长度计算出应存入的位置
③判断当前位置是否为null,如果是null直接存入,如果位置不为null,表示有元素,则调用equals方法比较属性值,如果一样,则不存,如果不一样,则存入数组。(JDK7新元素占老元素位置,指向老元素;JDK 8中新元素挂在老元素下面)
④当数组存满到16*0.75=12时,就自动扩容,每次扩容原先的两倍
随便个一个草图
一条线的就是链表,JDK8之前的,不是一条的就是红黑树,JDK8开始后的,至于这个位置怎么排的、谁指向谁,看上面的流程自己理解。
问题:
如果我们自己创建了一个类,但创建不同对象时有可能会出现内容相同的情况,但都是new出来的对象,地址是不同的,哈希值也就不同,但我们想把内容相同的排除该怎么办呢?
解决: 重写对象的hashCode和equals方法
(ieda中有快捷键,自己看看理解一下就行了)
步骤
①鼠标右键
②
特点:有序、不重复、无索引。
注: 这里的有序并不是位置按顺序,而是保证存储和取出的元素顺序一致,即每个元素结点存储上一个和下一个元素的地址,即双链表结构
原理
底层数据结构是依然哈希表,只是每个元素又额外的多了一个双链表的机制记录存储的顺序。
模型图
特点
注意: TreeSet集合是一定要排序的,可以将元素按照指定的规则进行排序。
TreeSet集合默认的规则
结论: 想要使用TreeSet存储自定义类型,需要制定排序规则
自定义排序规则(以Student类,比年龄为例)
方式一
class Student implements Comparable<Student>{
@Override
public int compareTo(Student o) {
return this.age-o.age;
}
方式二
TreeSet<Student> treeSet=new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.age-o2.age;
}
});
}
两种方式中,关于返回值的规则:
1)如果认为第一个元素大于第二个元素返回正整数即可。
2)如果认为第一个元素小于第二个元素返回负整数即可。
3)如果认为第一个元素等于第二个元素返回0即可,此时Treeset集合只会保留一个元素,认为两者重复。
注意: 如果TreeSet集合存储的对象有实现比较规则,集合也自带比较器,默认使用集合自带的比较器排序
如果希望元素可以重复,又有索引,索引查询要快
如果希望元素可以重复,又有索引,增删首尾操作快
如果希望增删改查都快,但是元素不重复、无序、无索引
如果希望增删改查都快,但是元素不重复、有序、无索引
如果要对对象进行排序