📚1.集合的理解
前面我们保存多个数据使用的是数组,那么数组有不足的地方呢?
📒1.1 数组
- 长度开始时必须指定,而且一旦指定,不能更改
- 保存的必须为同一类型的元素
- 使用数组进行增加/删除元素的示意代码-比较麻烦
分析:写出Person数组扩容示意代码。
Person[l pers = new Person[1];
per[0]=new Person();
如何增加新的Person对象?
Person[l pers2 = new Person[pers.length+1];
for()
pers2[pers2.length-1]=new Person()://添加新的对象
📒1.2 集合
- 可以动态保存任意多个对象,使用比较方便!
- 提供了一系列方便的操作对象的方法:add、remove、set、get等
- 使用集合添加,删除新元素的代码简洁了
📚2.集合的框架体系
Java 的集合类很多,主要分为两大类Collection和Map
- 集合主要是两组(单列集合,双列集合)
- Collection接口有两个重要的子接口 List Set,他们的实现子类都是单列集合
- Map接口的实现子类是双列集合,存放的 K-V
- 把梳理的两张图记住
🍀Collection接口
🍀Map接口
??代码示例:
import java.util.ArrayList;
import java.util.HashMap;
public class Collection_ {
@SuppressWarnings({"all"})
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add("jack");
arrayList.add("tom");
HashMap hashMap = new HashMap();
hashMap.put("NO1", "北京");
hashMap.put("NO2", "上海");
}
}
📚3.Collection 接口和常用方法
📒3.1 Collection 接口实现类的特点
1. Collection类是继承自lterable类的。
public interface Collection<E> extends lterable<E>
2. collection实现子类可以存放多个元素,每个元素可以是Object 3. 有些Collection的实现类,可以存放重复的元素,有些不可以 4. 有些Collection的实现类,有些是有序的(List),有些不是有序(Set) 5. Collection接口没有直接的实现子类,是通过它的子接口Set 和 List来实现的 ??代码示例:
import java.util.ArrayList;
import java.util.List;
public class CollectionMethod {
@SuppressWarnings({"all"})
public static void main(String[] args) {
List list = new ArrayList();
list.add("jack");
list.add(10);
list.add(true);
System.out.println("list=" + list);
list.remove(true);
System.out.println("list=" + list);
System.out.println(list.contains("jack"));
System.out.println(list.size());
System.out.println(list.isEmpty());
list.clear();
System.out.println("list=" + list);
ArrayList list2 = new ArrayList();
list2.add("红楼梦");
list2.add("三国演义");
list.addAll(list2);
System.out.println("list=" + list);
System.out.println(list.containsAll(list2));
list.add("聊斋");
list.removeAll(list2);
System.out.println("list=" + list);
}
}
??代码运行:
list=[jack, 10, true]
list=[jack, 10]
true
2
false
list=[]
list=[红楼梦, 三国演义]
true
list=[聊斋]
📒3.2 Collection 接口遍历元素方式
💡3.2.1 方法一:使用 Iterator(迭代器)
💭基本介绍: 1. Iterator对象称为迭代器,主要用于遍历Collection集合中的元素。 2. 所有实现了Collection接口的集合类都有一个iterator()方法,用以返回 一个实现了Iterator接口的对象,即可以返回一个迭代器。
3. Iterator的结构
4. Iterator仅用于遍历集合,Iterator本身并不存放对象。
💭Iterator接口的方法:
💭代码示例 Book类:
class Book {
private String name;
private String author;
private double price;
public Book(String name, String author, double price) {
this.name = name;
this.author = author;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", author='" + author + '\'' +
", price=" + price +
'}';
}
}
主类:
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class CollectionIterator {
@SuppressWarnings({"all"})
public static void main(String[] args) {
Collection col = new ArrayList();
col.add(new Book("三国演义", "罗贯中", 10.1));
col.add(new Book("小李飞刀", "古龙", 5.1));
col.add(new Book("红楼梦", "曹雪芹", 34.6));
Iterator iterator= col.iterator();
while (iterator.hasNext()){
Object obj=iterator.next();
System.out.println("obj="+obj);
}
iterator = col.iterator();
System.out.println("===第二次遍历===");
while (iterator.hasNext()) {
Object obj = iterator.next();
System.out.println("obj=" + obj);
}
}
}
运行结果:
obj=Book{name='三国演义', author='罗贯中', price=10.1}
obj=Book{name='小李飞刀', author='古龙', price=5.1}
obj=Book{name='红楼梦', author='曹雪芹', price=34.6}
💭分析: 上述代码中使用迭代器的过程: 第一个
Iterator iterator= col.iterator();
第二个
while (iterator.hasNext()){
Object obj=iterator.next();
System.out.println("obj="+obj);
}
这里告诉大家一个快速生成迭代器while循环的快捷键:itit 显示所有的快捷键的的快捷键:ctrl + j
💭另外的细节: 1.当退出while循环后,这时iterator迭代器,指向最后的元素
iterator.next();
2.如果希望再次遍历,需要重置我们的迭代器
iterator = col.iterator();
System.out.println("===第二次遍历===");
while (iterator.hasNext()) {
Object obj = iterator.next();
System.out.println("obj=" + obj);
}
💡3.2.2 方法二:增强for循环
💭基本介绍: 增强for循环,可以代替iterator迭代器,特点:增强for就是简化版的iterator本质一样,只能用于遍历集合或数组。 💭基本语法:
for(元素类型元素名:集合名或数组名){
访问元素
}
💭代码示例:
import java.util.ArrayList;
import java.util.Collection;
public class 增强for {
public static void main(String[] args) {
Collection col = new ArrayList();
col.add(new Book("三国演义", "罗贯中", 10.1));
col.add(new Book("小李飞刀", "古龙", 5.1));
col.add(new Book("红楼梦", "曹雪芹", 34.6));
for(Object book:col){
System.out.println("book"+book);
}
int[] nums={1,2,3,4};
for(int i:nums){
System.out.println(i);
}
}
}
💭运行结果:
bookBook{name='三国演义', author='罗贯中', price=10.1}
bookBook{name='小李飞刀', author='古龙', price=5.1}
bookBook{name='红楼梦', author='曹雪芹', price=34.6}
1
2
3
4
📚4.List 接口和常用方法
📒4.1List接口的基本介绍
- List 接口是 Collection接口的子接口
- List集合类中元素有序(即添加顺序和取出顺序一致)、且可重复
import java.util.ArrayList;
import java.util.List;
public class List_ {
public static void main(String[] args) {
List list = new ArrayList();
list.add("jack");
list.add("tom");
list.add("mary");
list.add("hj");
list.add("tom");
System.out.println("list=" + list);
System.out.println(list.get(3));
}
}
运行结果:
list=[jack, tom, mary, hj, tom]
hj
- List集合中的每个元素都有其对应的顺序索引,即支持索引
- List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
- JDK API中List接口的实现类有:
📒4.2 List 接口的常用方法
List集合里添加了一些根据索引来操作集合元素的方法
-
void add(int index, Object ele):在index位置插入ele元素 -
boolean addAll(int index,Collection eles):从index位置开始将 eles中的所有元素添加进来 -
Object get(int index):获取指定index位置的元素 -
int indexOf(Object obj):返回obj在集合中首次出现的位置 -
int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置 -
Object remove(int index):移除指定index位置的元素,并返回此元素 -
Object set(int index, Object ele):设置指定index位置的元素为ele 相当于是替换. -
List subList(int fromlndex, int tolndex):返回从fromIndex到 tolndex位置的子集合
import java.util.ArrayList;
import java.util.List;
public class ListMethod {
public static void main(String[] args) {
List list=new ArrayList();
list.add("张三丰");
list.add("贾宝玉");
list.add(1,"胡坚");
System.out.println("list="+list);
List list2 = new ArrayList();
list2.add("jack");
list2.add("tom");
list.addAll(1, list2);
System.out.println("list=" + list);
System.out.println(list.get(0));
System.out.println(list.indexOf("tom"));
list.add("胡坚");
System.out.println("list=" + list);
System.out.println(list.lastIndexOf("胡坚"));
list.remove(0);
System.out.println("list=" + list);
list.set(1, "玛丽");
System.out.println("list=" + list);
List returnlist = list.subList(0, 2);
System.out.println("returnlist=" + returnlist);
}
}
📒4.3 List 接口练习
添加10个以上的元素(比如String “hello” ),在2号位插入一个元素"hj",获得第5个元素,删除第6个元素,修改第7个元素,在使用迭代器遍历集合,要求:使用List的实现类ArrayList完成。
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ListExercise {
@SuppressWarnings({"all"})
public static void main(String[] args) {
List list = new ArrayList();
for (int i = 0; i < 12; i++) {
list.add("hello" + i);
}
System.out.println("list=" + list);
list.add(1, "韩顺平教育");
System.out.println("list=" + list);
System.out.println("第五个元素=" + list.get(4));
list.remove(5);
System.out.println("list=" + list);
list.set(6, "三国演义");
System.out.println("list=" + list);
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
System.out.println("obj=" + obj);
}
}
}
📒4.4 List 的三种遍历
方式一:使用iterator
lterator iter = col.iterator();
while(iter.hasNext(){
Object o = iter.next(;
}
方式二:使用增强for
for(Object o:col){}
方式三:使用普通for
for(int i=0;i<list.size();i++) {
Object object = list.get(i);
System.out.println(object);
}
import java.util.*;
public class ListFor {
@SuppressWarnings({"all"})
public static void main(String[] args) {
List list = new LinkedList();
list.add("jack");
list.add("tom");
list.add("鱼香肉丝");
list.add("北京烤鸭子");
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
System.out.println(obj);
}
System.out.println("=====增强 for=====");
for (Object o : list) {
System.out.println("o=" + o);
}
System.out.println("=====普通 for====");
for (int i = 0; i < list.size(); i++) {
System.out.println("对象=" + list.get(i));
}
}
}
说明:使用LinkedList完成使用方式和ArrayList一样
📒4.5 实现类的练习
Book类:
class Book {
private String name;
private String author;
private double price;
public Book(String name, String author, double price) {
this.name = name;
this.author = author;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "name='" + name + '\'' +
", author='" + author + '\'' +
", price=" + price
;
}
}
实现类:
import java.util.ArrayList;
import java.util.List;
public class 练习 {
public static void main(String[] args) {
List list = new ArrayList();
list.add(new Book("红楼梦", "曹雪芹", 100));
list.add(new Book("西游记", "吴承恩", 10));
list.add(new Book("水浒传", "施耐庵", 19));
list.add(new Book("三国", "罗贯中", 80));
sort(list);
for(Object o:list){
System.out.println(o);
}
}
public static void sort(List list){
int listsize=list.size();
for(int i=0;i<listsize-1;i++){
for(int j=0;j<listsize-1-i;j++){
Book book1=(Book) list.get(j);
Book book2=(Book) list.get(j+1);
if(book1.getPrice()>book2.getPrice()){
list.set(j,book2);
list.set(j+1,book1);
}
}
}
}
}
运行结果:
name='西游记', author='吴承恩', price=10.0
name='水浒传', author='施耐庵', price=19.0
name='三国', author='罗贯中', price=80.0
name='红楼梦', author='曹雪芹', price=100.0
📚5.ArrayList 底层结构和源码分析
📒5.1 ArrayList 的注意事项
1. permits all elements, including null ,ArrayList可以加入null,并且多个
import java.util.ArrayList;
public class ArrayListDetail {
public static void main(String[] args) {
ArrayList arrayList=new ArrayList();
arrayList.add(null);
arrayList.add("hj");
arrayList.add(null);
System.out.println(arrayList);
}
}
2. ArrayList是由数组来实现数据存储的 3. ArrayList基本等同于Vector,除了ArrayList是线程不安全(执行效率高)看源码,没有synchronized.在多线程情况下,不建议使用ArrayList.
📒5.2 ArrayList 的底层操作机制源码分析(重点)
先说结论,再分析源码(示意图)
- ArrayList中维护了一个0bject类型的数组elementData. [debug看源码]
transient Object[] elementData;
- 当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第1次添加,则扩容elementData为10,如需要再次扩容,则扩容elementData为1.5倍。
- 如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍。
建议:自己去debug一把我们的ArrayList的创建和扩容的流程.
import java.util.ArrayList;
@SuppressWarnings({"all"})
public class ArrayListSource {
public static void main(String[] args) {
ArrayList list = new ArrayList();
for (int i = 1; i <= 10; i++) {
list.add(i);
}
for (int i = 11; i <= 15; i++) {
list.add(i);
}
list.add(100);
list.add(200);
list.add(null);
}
}
💡源码分析
建议看视频讲解:韩顺平Java_ArrayList底层源码 分析使用无参构造器,创建和使用ArrayList的源码: 首先在ArrayList创建的语句加上断点,再debug上面的代码。(不会调试的去学习一下再来看) 点击step into,就会跳转至ArrayList源码。 进入第一个for循环后会调用list.add(): 跳入ensureCapacityInternal(size + 1); 再跳入ensureExplicitCapacity(int mincapacity) : 看一下grow(int minCapacity) :
分析使用有参构造器,创建和使用ArrayList 的源码:
📚6.Vector 底层结构和源码剖析
📒6.1Vector基本介绍
1. Vector类的定义说明 2. Vector底层也是一个对象数组
protected object[] elementData;
3. Vector是线程同步的,即线程安全, Vector类的操作方法带有
synchronizedpublic synchronized E get(int index) {
if (index >= elementCount)
throw new ArraylndexOutOfBoundsException(index);
return elementData(index);
}
4. 在开发中,需要线程同步安全时,考虑使用Vector
📒6.2Vector底层结构和ArrayList的比较
📒6.3分析
import java.util.Vector;
@SuppressWarnings({"all"})
public class Vector_ {
public static void main(String[] args) {
Vector vector = new Vector(8);
for (int i = 0; i < 10; i++) {
vector.add(i);
}
vector.add(100);
System.out.println("vector=" + vector);
}
}
📚7.LinkedList底层结构(维护了一个双向链表)
📒7.1LinkedList的底层操作机制
-
LinkedList底层维护了一个双向链表. -
LinkedList中维护了两个属性first和last分别指向首节点和尾节点 -
每个节点(Node对象),里面又维护了prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个节点。最终实现双向链表. -
所以LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高
📒7.2 模拟一个简单的双向链表
定义一个 Node 类,Node 对象 表示双向链表的一个结点:
class Node {
public Object item;
public Node next;
public Node pre;
public Node(Object name) {
this.item = name;
}
public String toString() {
return "Node name=" + item;
}
}
模拟一个简单的双向链表:
public class LinkedList01 {
public static void main(String[] args) {
Node jack = new Node("jack");
Node tom = new Node("tom");
Node hsp = new Node("老韩");
jack.next = tom;
tom.next = hsp;
hsp.pre = tom;
tom.pre = jack;
Node first = jack;
Node last = hsp;
}
}
演示从头到尾进行遍历和从头到尾进行遍历
public class LinkedList01 {
public static void main(String[] args) {
Node jack = new Node("jack");
Node tom = new Node("tom");
Node hsp = new Node("老韩");
jack.next = tom;
tom.next = hsp;
hsp.pre = tom;
tom.pre = jack;
Node first = jack;
Node last = hsp;
while (true) {
if (first == null) {
break;
}
System.out.println(first);
first = first.next;
}
while (true) {
if (last == null) {
break;
}
System.out.println(last);
last = last.pre;
}
}
}
运行结果:
Node name=jack
Node name=tom
Node name=老韩
Node name=老韩
Node name=tom
Node name=jack
演示链表的添加对象(数据):
public class LinkedList01 {
public static void main(String[] args) {
Node jack = new Node("jack");
Node tom = new Node("tom");
Node hsp = new Node("老韩");
jack.next = tom;
tom.next = hsp;
hsp.pre = tom;
tom.pre = jack;
Node first = jack;
Node last = hsp;
Node smith = new Node("smith");
smith.next = hsp;
smith.pre = tom;
hsp.pre = smith;
tom.next = smith;
first = jack;
System.out.println("===从头到尾进行遍历===");
while (true) {
if (first == null) {
break;
}
System.out.println(first);
first = first.next;
}
last = hsp;
System.out.println("====从尾到头的遍历====");
while (true) {
if (last == null) {
break;
}
System.out.println(last);
last = last.pre;
}
}
}
📒7.3 LinkedList 的增删改查案例
import java.util.Iterator;
import java.util.LinkedList;
@SuppressWarnings({"all"})
public class LinkedListCRUD {
public static void main(String[] args) {
LinkedList linkedList = new LinkedList();
linkedList.add(1);
linkedList.add(2);
linkedList.add(3);
System.out.println("linkedList=" + linkedList);
linkedList.remove();
System.out.println("linkedList=" + linkedList);
linkedList.set(1, 999);
System.out.println("linkedList=" + linkedList);
Object o = linkedList.get(1);
System.out.println(o);
System.out.println("===LinkeList 遍历迭代器====");
Iterator iterator = linkedList.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println("next=" + next);
}
System.out.println("===LinkeList 遍历增强 for====");
for (Object o1 : linkedList) {
System.out.println("o1=" + o1);
}
System.out.println("===LinkeList 遍历普通 for====");
for (int i = 0; i < linkedList.size(); i++) {
System.out.println(linkedList.get(i));
}
}
}
📒7.4 源码分析
建议看视频讲解:韩顺平Java_LinkedList源码图解 创建:
LinkedList linkedList = new LinkedList();
调用无参构造器 public LinkedList() {}
增加:
执行 添加
public boolean add (E e){
linkLast(e);
return true;
}
将新的结点,加入到双向链表的最后
void linkLast (E e){
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
删除:
linkedList.remove();
1. 执行 removeFirst
public E remove() {
return removeFirst();
}
2. 执行
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
3. 执行 unlinkFirst, 将 f 指向的双向链表的第一个结点拿掉
private E unlinkFirst(Node<E> f) {
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null;
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
📒7.5 ArrayList 和 LinkedList 的比较
如何选择ArrayList和LinkedList:
- 如果我们改查的操作多,选择ArrayList
- 如果我们增删的操作多,选择LinkedList
- 一般来说,在程序中,80%-90%都是查询,因此大部分情况下会选择ArrayList
- 在一个项目中,根据业务灵活选择,也可能这样,一个模块使用的是ArrayList,另外一个模块是LinkedList,也就是说,要根据业务来进行选择
|