集合的概念
关于集合,首先我们得知道什么是集合,集合的概念是什么,这是最基础的,也是最基本的,那后面面试官问什么,才有可能一一答来。那什么是集合呢?
- 集合类存放在java.util包中
- 集合类主要有3种:set(集)、list(列表)、map(映射)
- 集合存放的都是对象的引用,而非对象的本身。所以我们称集合中的对象就是对象的引用。
简单的说:集合就是一个放数据对象引用的容器,不能放基本数据类型,只能放对象。而数据可以放基本数据类型。
集合的简要结构图
集合的应用场景
从以上就可以看到,如果前端有一个按钮需要查询所有学生信息,那具体的逻辑代码的实现就需要后端实现,后端需要访问数据库,数据库会查询出所有学生信息,然后再返回给后端接口,后端接口再把每一条数据返给前端,过于麻烦,浪费时间,我们就可以把这些数据放到一个集合里,然后只需要把这个集合返回给前端,让前端遍历就行了。
Collection接口
collection常用方法
public class demo {
public static void main(String[] args) {
/** * 集合collection常用的方法: * 增加:add(E e)、addAll(Collection<? extends E> c) * 删除:clear() 、remove(Object o) * 修改: * 判断:contains(Object o)、equals(Object o)、isEmpty() * 查看:iterator()、size() */
//add(E e):向集合中添加一个元素
//创建一个集合对象,接口不能创建对象,只能利用实现类创建对象
Collection collection = new ArrayList();
//集合有一个特点,只能存放引用数据类型的数据,不能是基本数据类型的数据。
//基本数据类型会自动装箱,对应包装类。 int————integer
collection.add(13);
collection.add(14);
System.out.println("========add======"+collection);
//addAll(Collection<? extends E> c),将另外一个集合也添加到此集合中
//新建一个集合,采用把数组转为为集合的方式
List<Integer> lists = Arrays.asList(new Integer[]{ 1, 4, 6});
collection.addAll(lists);
System.out.println("=====addAll===="+collection);
//clear():清空集合中的数据
collection.clear();
//isEmpty():判断集合是否为空
System.out.println("=========isEmpty======"+collection.isEmpty());
//size():查询集合中数据的个数
System.out.println("==========size()========"+collection.size());
}
}
运行结果如下:
collection集合的遍历方式
public class demo2 {
public static void main(String[] args) {
Collection collection = new ArrayList();
collection.add(13);
collection.add(14);
collection.add("mengmeng");
//方式一:增强for循环
for(Object o:collection){
System.out.print(o+"\t");
}
System.out.println();
System.out.println("------------------------");
//方式二:iterator()
Iterator iterator = collection.iterator();
while (iterator.hasNext()){
System.out.print(iterator.next()+"\t");
}
}
}
我们需要说一下iterator()它的原理,iterator也叫做迭代器。前面我们说过collection底层是数组的数据结构,现在我们通过iterator迭代器遍历集合,先从头的部分,也就是下标为0的位置开始遍历,iterator里面有一个方法hasNext,通过这个方法来判断有没有下一个元素,如果有返回true,没有的话就返回false。若是返回true,说明有元素,就可以把当前元素输出在控制台,也就是通过next方法,并移动到下一个下标的位置重新进行判断,以此类推。直到移动到最后一个位置时,即hasNext返回false时,此时才是遍历完毕。
List接口
List接口常用的方法
public class demo3 {
public static void main(String[] args) {
/** * 集合List常用的方法: * 增加:add(int index, E element) * 删除:remove(int index) 、remove(Object o)从collection中继承过来的 * 修改:set(int index, E element) * 判断: * 查看:get(int index) */
List list = new ArrayList<>();
list.add(1);
list.add(4);
list.add(6);
list.add(2);
list.add("abc");
System.out.println("原来集合的数据:"+list);
//add(int index, E element):在指定的位置插入一个数据
list.add(2,"mengmeng");
System.out.println("插入数据之后集合的数据:"+list);
//set(int index, E element):修改指定位置的数据
list.set(3,8);
System.out.println("修改之后的集合数据:"+list);
//remove(int index):当集合中存放的是Integer数据类型时,调用这个移除方法
list.remove(2);
System.out.println("移除数据之后集合的数据:"+list);
//remove(Object o)
list.remove("abc");
System.out.println("移除数据之后集合中的数据:"+list);
//List的遍历
//方式一:for循环
System.out.println("遍历list集合的方式一:");
for (int i=0;i<list.size();i++){
System.out.print(list.get(i)+"\t");
}
//方式二:增强for循环
System.out.println();
System.out.println("遍历list集合的方式二:");
for (Object obj:list) {
System.out.print(obj+"\t");
}
//方式三:迭代器iterator
System.out.println();
System.out.println("遍历list集合的方式三");
Iterator iterator = list.iterator();
while (iterator.hasNext()){
System.out.print(iterator.next()+"\t");
}
}
}
ArrayList实现类
JDK1.7时的源码
1,我们首先先把idea中的jdk版本切换到1.7,切换方式如下: 2,ArrayList实现接口有失误,即父类AbstractList已经实现了接口List,而ArrayList又实现了一次。集合的创始人也承认了这个问题,但是后期jdk更新时,也没有做修改,觉得没毕业。
3,底层有两个重要的属性:elementData和size,即ArrayList底层是数组的重要原因。
4,在jdk1.7时,调用构造器时,会给底层数据elementData初始化数据为10。 5,往arrayList数组里添加数据时,调用底层的代码,如下:
6,当底层数组初始化的10个位置都满的时候就开始进行数组的扩容了,扩容长度为原数组的1.5倍。
<font size="3" color="red"> 小总结:jdk1.7时,ArrayList特点: 1,底层是数组的数据结构。 2,在调用构造器的时候,数组初始化长度为10。 3,扩容的时候,扩展为原数组的1.5倍。</font>
JDK1.8时的源码
1,底层依旧是Object类型的数组,源码中仍旧有两个重要的参数:elementData和size。 2,使用代码ArrayList arrayList = new ArrayList();调用无参的构造器的时候,底层初始化的是一个长度为0空的数组,不再是直接赋值10。
<font size="3"> 3,调用add方法时,才完成了对底层数组初始化赋值的一个过程,赋值为10。 </font>
<font size="3" color="red"> 小总结:jdk1.8时,ArrayList特点: 1,底层依旧是一个数组类型的数据结构,但是是一个空的数组。 2,调用add方法之后才重新为数组赋值,新数组的长度为10,节省了内存。</font>
区别:1.7在调用构造器的时候就给底层数组初始化为10,不使用数组的时候也会初始化,会造成资源浪费。1.8时是在add方法时才初始化为10。
Vector实现类
底层源码
1,底层依旧是一个数组的数据结构,用elementData表示数组中的实际有效长度
2, Vector vector = new Vector();调用构造器的时候为底层数组初始化长度为10。
3,add()方法添加数据时,达到底层数组长度的最大值时,底层数组开始扩容,扩展为原数组长度的2倍,这是与ArrayList的区别。
4,是线程安全的。
Vector与ArrayList的区别
1,ArrayList底层扩容是原来的1.5倍,Vector是原来的2倍。 2,ArrayList不是线程安全的,但效率高,Vector是线程安全的,但效率低。
Vector与ArrayList的优点
由于两者底层都是数组的数据结构,所以它们的优缺点与数组也类似: 1,查询的效率高,但是插入、删除时效率低。 2,数据内容可以重复。
LinkedList集合
LinkedList常用的方法
public class Demo5 {
/** * LinkedList集合常用的方法: * 增加:addFirst(E e) 、addLast(E e) * offer(E e) 、offerFirst(E e)、offerLast(E e) * 删除:poll()、pollFirst()、pollLast() * removeFirst()、removeLast() * 修改: * 查看:element()、getFirst()、getLast() * indexOf(Object o)、lastIndexOf(Object o) * peek()、E peekFirst() 、peekLast() * 判断: */
public static void main(String[] args) {
//创建一个LinkedList集合,因为底层代码是一个泛型,所以我们一般创建对象时也是创建一个泛型集合
LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("q");
linkedList.add("qw");
linkedList.add("qw");//可以添加重复数据
//addFirst(E e) 、addLast(E e) 向首部、尾部插入数据
linkedList.addFirst("a");
linkedList.addLast("z");
System.out.println("============"+linkedList);
//offer(E e) 、offerFirst(E e)、offerLast(E e) 分别在尾部、首部、尾部添加数据
linkedList.offer("x");
linkedList.offerFirst("b");
linkedList.offerLast("y");
System.out.println("============"+linkedList);
//poll()、pollFirst()、pollLast() 分别是删除首部、首部、尾部的数据
System.out.println("=============poll()删除的元素是:"+linkedList.poll());
System.out.println("=============pollFirst()删除的元素是:"+linkedList.pollFirst());
System.out.println("=============pollLast()删除的元素是:"+linkedList.pollLast());
System.out.println("=============removeFirst()删除:"+linkedList.removeFirst());
}
}
问题:pollFirst()和removeFirst()都是表示删除的,为什么要存在两组呢?它们有什么区别吗?
回答:当数组为空的时候,用pollFirst()可以删除null,代码不会报错,用removeFirst()删除的时候,代码会报错。pollFirst()是jdk1.6以后提出的,提高了代码的健壮性。
LinkedList底层源码
首先我们先大致简要的说一下Linkedlist的底层数据结构。下面这个图是梦梦在网上看视频的时候截的一张图,感觉还挺不错的,就拿来用了。 总结:Linkedlist的底层是一个双向链表的结构,双向的意思是:能够前后分别指向对象的链表地址,所以LinkedList底层是靠一个一个简要的对象连接起来。
未完,后续在路上。。。。