集合
1. 集合的理解和好处
-
数组
-
长度开始时必须指定,而且一旦指定,不能更改;
-
保存的必须为同一类型的元素
-
使用数组进行增加元素的示意代码-----比较麻烦
-
集合
-
可以动态保存任意多个对象,使用比较方便
-
提供了一系列方便的操作对象:add,remove,set,get等
-
使用集合添加,删除新元素的示意代码----简洁了
2. 集合的框架体系
java的集合类很多,主要分为两大类,如图:
-
集合主要是两组(单列集合,双列集合);
-
Collection接口有两个重要的子接口,List,Set,他们的实现子类都是单列集合
-
Map接口的实现子类 是双列集合,存放的K-V
3. Collection接口和常用方法
3.1 Collection接口实现类的特点
public interface Collection<E> extends Iterable<E>
-
collection实现子类可以存放多个元素,每个元素可以是object;
-
有些Collection的实现类,可以存放重复的元素,有些不可以;
-
有些Collection的实现类,有些是有序的(List),有些不是有序的(Set);
-
Collection接口没有直接的实现类,是通过它的子接口Set和List来实现的;
3.2 Collection接口和常用方法
Collection接口常用方法,以实现子类ArrayList来演示.
-
add:添加单个元素
-
remove:删除指定元素
-
contains:查找元素是否存在
-
size:获取元素个数
-
isEmpty:判断是否为空
-
clear:清空
-
addAll:添加多个元素
-
containsAll:查找多个元素是否都存在
-
removeAll:删除多个元素
public class CollectionMethod {
public static void main(String[] args) {
//add:添加单个元素
List list = new ArrayList();
list.add("jack");
list.add(10);//list.add(new Integer(10)) //自动装箱拆箱
list.add(true);
System.out.println("list="+list);
//remove:删除单个元素
// list.remove(0);//删除第一个元素
list.remove(true);//自动装箱拆箱
// System.out.println("list="+list);
//contains:查找摸个元素是否存在
System.out.println(list.contains("jack"));
//size:获取元素个数
System.out.println(list.size());
//isEmpty:判断是否为空
System.out.println(list.isEmpty());
//clear:清空
list.clear();
System.out.println("list="+list);
//addAll:添加多个元素
ArrayList list2 = new ArrayList();
list2.add("红楼梦");
list2.add("西游记");
list2.add("三国演义");
list2.add("水浒传");
list.addAll(list2);
System.out.println("list="+list);
//containsAll:查找多个元素是否存在
System.out.println(list.containsAll(list2));
//removeALL:删除多个元素
ArrayList list3 = new ArrayList();
list3.add("水浒传");
list3.add("三国演义");
list.removeAll(list3);
System.out.println("list="+list);
}
}
3.3 Colletion接口遍历方式1-使用Iterator(迭代器)
1. 基本介绍
-
Iterator对象称为迭代器,主要用于遍历Collection集合中的元素
-
所有实现了Collection接口的集合类都有一个Iterator()方法,用于返回一个实现了Iterator接口的对象,即可以
返回一个迭代器
-
Iterator的结构
-
Iterator仅用于遍历集合,Iterator本身并不存放对象;
2. 迭代器的执行原理
-
当创建完成指向某个集合或者容器的Iterator对象是,这是的指针其实指向的是第一个元素的上方,即指向一个 空
-
当调用hasNext方法的时候,只是判断下一个元素的有无,并不移动指针
-
当调用next方法的时候,向下移动指针,并且返回指针指向的元素,如果指针指向的内存中没有元素,会报异常。
-
remove方法删除的元素是指针指向的元素。如果当前指针指向的内存中没有元素,那么会抛出异常。
Iterator iterator = coll.iterator();//得到一个集合的迭代器
//快捷键itit:快速遍历迭代器
while (iterator.hasNext()){//判断是否还有下一个元素
Object obj = iterator.next();//1.指针下移 2.将下移以后集合位置上的元素返回
System.out.println(obj);
}
3. Iterator接口的方法
hasNext:没有指针下移操作,只是判断是否存在下一个元素 next:指针下移,返回该指针所指向的元素 remove:删除当前指针所指向的元素,一般和next方法一起用,这时候的作用就是删除next方法返回的元素
注意:在调用iterator.next()方法之前必须要调用iterator.hasNext()进行检测.若不调用,且下一条记录无效,直接调用iterator.next()会抛出NoSuchElementException异常;
public class CollectionIterator {
public static void main(String[] args) {
Collection coll = new ArrayList();
coll.add(new Book("小李飞刀", "古龙", 5.1));
coll.add(new Book("红楼梦", "曹雪芹", 34.6));
coll.add(new Book("三国演义", "罗贯中", 10.1));
//System.out.println(collection);
//先得到coll对应的迭代器
Iterator iterator = coll.iterator();
while (iterator.hasNext()){//判断是否还有下一个元素
//返回下一个元素,类型是Object
Object obj = iterator.next();
System.out.println(obj);
}
//当退出while循环后,这时Iterator迭代器,指向最后的元素
//iterator.next();//NoSuchElementException
//如果希望再次遍历,需要重置我们的迭代器
iterator = coll.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
}
}
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;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", author='" + author + '\'' +
", 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;
}
}
3.4 Collection接口遍历对象方式2-for循环增强
增强for循环,可以代替Iterator迭代器,特点:增强for就是简化版的Iterator,本质一样,只能用于遍历集合或数组;
1. 基本语法
for(元素类型 元素名 : 集合名或数组名){
访问元素
}
2.底层原理
-
使用增强For,在Collection集合
-
增强for,底层仍然是迭代器
-
增强for可以理解成就是简化版本的迭代器遍历
-
快捷方式 I 或者 集合对象.for
-
增强for也可以直接在数组中使用
public class CollectionFor {
public static void main(String[] args) {
Collection coll = new ArrayList();
coll.add(new Book("小李飞刀", "古龙", 5.1));
coll.add(new Book("红楼梦", "曹雪芹", 34.6));
coll.add(new Book("三国演义", "罗贯中", 10.1));
//1.使用增强For,在Collection集合
//2.增强for,底层仍然是迭代器
//3.增强for可以理解成就是简化版本的迭代器遍历
//4.快捷方式 I 或者 集合对象.for
for(Object book:coll){
System.out.println("book"+book);
}
//增强for也可以直接在数组中使用
int[] nums = {1,8,10,20};
for(int i:nums){
System.out.println(i);
}
}
}
4. List接口和常用方法
4.1 List接口基本介绍
List接口是Collection接口的子接口
-
List集合中元素有序(即添加顺序和取出顺序一致),且可重复;
-
List集合中的每个元素都有器对应的顺序索引,即支持索引;
-
List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素
-
JDK API中List接口的实现类有很多,常用的有:ArrayList,LinkedList,Vector;
4.2 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 fromIndex,int toIndex):返回从fromIndex到toIndex位置的子集合; //注意:返回的子集合 包左不包右 fromIndex <= subList < toIndex
public class ListMethod {
@SuppressWarnings("all")
public static void main(String[] args) {
List list = new ArrayList();
list.add("张三丰");
list.add("贾宝玉");
list.add("张三");
//1.void add(int index,Object ele):在index位置插入ele元素
//在index = 1的位置插入一个对象
list.add(1,"张三");
System.out.println("list="+list);
System.out.println("----------------");
//2.boolean addAll(int index,Collection eles):从index位置开始将eles中的所有元素添加进来
List list2 = new ArrayList();
list2.add("jack");
list2.add("tom");
list.addAll(1,list2);
System.out.println("list="+list);
System.out.println("----------------");
//3.Object get(int index):获取指定index位置的元素
//4.int indexOf(Object obj):返回obj在集合中首次出现的位置
System.out.println(list.indexOf("tom"));//2
System.out.println("----------------");
//5.int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
System.out.println(list);
System.out.println(list.lastIndexOf("张三"));//5
System.out.println("----------------");
//6. Object remove(int index):移除指定index位置的元素,并返回此元素
list.remove(0);
System.out.println("remove:"+list);
System.out.println("----------------");
//7.Object set(int index,Object ele):设置指定index位置的元素为ele,相当于是替换
list.set(1,"阿狸");
System.out.println(list);
System.out.println("----------------");
//8.List subList(int fromIndex,int toIndex):返回从fromIndex到toIndex位置的子集合
//注意:返回的子集合 包左不包右 fromIndex <= subList < toIndex
System.out.println(list);
List resList = list.subList(1,list.size());
System.out.println(resList);
}
}
4.3 List的三种遍历方式
List接口实现子类[ArrayList,LinkedList,Vector],遍历方式相同
-
方式一:使用Iterator
Iterator iter = list.Iterator();
while(iter.hasNext()){
• Object 0 = iter.next();
}
-
方式二:使用增强for
for(Object o : list){
}
-
方式三:使用普通for
for (int i = 0; i < list.size(); i++) {
Object obj = list.get(i);
System.out.println(obj);
}
说明:使用LinkedList完成使用方式和ArrayList一样
5. ArrayList底层结构和源码分析
5.1 ArrayList的注意事项
-
permits all elements,including null;(允许所有元素,包括null),ArrayList可以加入null,并且多个;
-
ArrayList是由数组来实现数据存储的
-
ArrayList基本等同于Vector,除了ArrayList是线程不安全(执行效率高)看源码,没有synchronized关键字修饰
在多线程情况下,不建议使用ArrayList;
5.2 ArrayList的底层操作机制源码分析(重点,难点)
-
ArrayList中维护了一个Object类型的elementData数组
transient Object[] elementData;//transient 表示瞬间,短暂的,表示该属性不会被序列化
(Java序列化是指把Java对象转换为字节序列的过程,而Java反序列化是指把字节序列恢复为Java对象的过程)
-
当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第一次添加,则扩容elementData为10,则需要再次扩容,则扩容elementData为1.5倍;
-
如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍;
6. Vector底层结构和源码剖析
6.1 Vector的基本介绍
-
Vector类的定义说明;
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
-
Vector底层也是一个对象数组;
protected Object[] elementData;
-
Vector是线程同步,即线程安全,Vector类的操作方法带有synchronized;
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return elementData(index);
}
-
在开发中,需要线程同步安全时,考虑使用Vector;
6.2 Vector和ArrayList的比较
6.3 Vector底层结构
public class Vector_ {
public static void main(String[] args) {
//无参构造器
//Vector vector = new Vector();
Vector vector = new Vector(10);
for (int i = 0; i < 10; i++) {
vector.add(i);
}
vector.add(100);
System.out.println("vector="+vector);
//解读源码
/*
//1. new Vector() 底层
public Vector() {
this(10);
}
补充:如果是有参数构造 Vector vector = new Vector(8);
走的方法是:
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
2.vector.add(i)
2.1 //下面这个方法就添加数据到vector集合
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
2.2 //确定是否需要扩容 条件 : minCapacity - elementData.length > 0
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
2.3 //如果需要的数组大小 不够用,就扩容 ,扩容的算法
//newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
//就是扩容两倍
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
*/
}
}
7. LinkedList底层结构
7.1 LinkedList的全面说明
-
LinkedList底层实现了双向链表和双端队列特点;
-
可以添加任意元素(元素可以重复,包括null);
-
线程不安全,没有实现同步;
7.2LinkedList的底层操作机制
-
LinkedList底层维护了一个双向链表;
-
LinkedList中维护了两个属性first和last分别指向首节点和尾结点;
-
每个节点(Node对象),里面又维护了prev,next,item三个属性,其中通过prev指向前一个,通过next指向后一个节点,最终实现双向链表;
-
所以LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高;
-
模拟一个简单的双向链表;
public class LinkedList01 {
public static void main(String[] args) {
//模拟一个简单的双向链表
Node jack = new Node("jack");
Node tom = new Node("tom");
Node mary = new Node("mary");
//连接三个节点,形成双向链表
//jack -> tom -> mary
jack.next = tom;
tom.next = mary;
//mary -> tom -> jack
mary.pre = tom;
tom.pre = jack;
Node first = jack;//让first引用指向jack,就是双向链表的头结点
Node last = mary;//让last引用指向mary,就是双向链表的尾结点
//演示 , 从头到尾遍历
System.out.println("从头到尾遍历===================");
while (true) {
if (first == null) {
break;
}
System.out.println(first);
first = first.next;
}
//演示 , 从尾到头遍历
System.out.println("从尾到头遍历===================");
while (true) {
if (last == null) {
break;
}
System.out.println(last);
last = last.pre;
}
//演示链表的添加对象/数据,是多么方便
//要求,在tom 和 mary之间 插入一个对象 smith
//1.先创建一个Node结点,name就是smith
Node smith = new Node("smith");
//下面就把smith加入双向链表了
tom.next = smith;
smith.pre = tom;
smith.next = mary;
mary.pre=smith;
System.out.println("===========================");
//让fist再次指向jack
first = jack;//重置一下first
while (true) {
if (first == null) {
break;
}
System.out.println(first);
first = first.next;
}
}
}
//定义一个Node类,Node对象 表示双向链表的一个节点
class Node {
public Object item;//真正存放数据
public Node next;//指向后一个节点
public Node pre;//指向前一个节点
public Node(Object item) {
this.item = item;
}
@Override
public String toString() {
return "Node name=" + item;
}
}
7.3 LinkedList底层结构
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();//这里默认删除的是第一个结点
//linkedList.remove(2);
System.out.println("LinkedList="+linkedList);
//修改某个结点
linkedList.set(1,999);
System.out.println("LinkedList="+linkedList);
//得到某个结点对象
//get(1)是得到双向链表的第二个对象
Object o = linkedList.get(1);
System.out.println(o);//999
//因为LinkedList是实现了List接口 ,遍历方式
System.out.println("====LinkedList遍历迭代器====");
Iterator iterator = linkedList.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
System.out.println("====LinkedList遍历增强For====");
for (Object oo :linkedList) {
System.out.println(oo);
}
System.out.println("====LinkedList遍历普通for====");
for (int i = 0; i < linkedList.size(); i++) {
System.out.println(linkedList.get(i));
}
//源码阅读
/*
1. LinkedList linkedList = new LinkedList();
public LinkedList(){}
2.这时LinkedList的属性:first = null last=null
3.执行添加方法
public boolean add(E e) {
linkLast(e);
return true;
}
4.将新的结点,加入到双向链表的最后;
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) {
// assert f == first && f != null;
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
*/
}
}
7.4 ArrayList和LinkedList的比较
-
ArrayList和LinkedList的比较
ArrayList改查的效率较高,因为可以通过索引直接定位;LinkedList改查效率较低,会从头开始,一个一个遍历;
-
如何选择ArrayList和LinkedList:
-
如果我们改查的操作多,选择ArrayList;
-
如果我们增删的操作多,选择LinkedList;
-
一般来说,在程序中,80%-90%都是查询,因此大多数情况下会选择ArrayList;
-
在一个项目中,根据业务灵活选择,也可以这样,一个模块下使用的是ArrayList,另一个模块下选择的是LinkedList,也就是说,要根据业务来进行选择;
8. Set接口和常用方法
8.1 Set接口基本介绍
-
无序(添加和取出顺序不一致),没有索引;
-
不允许重复元素,所以最多包含一个null;
-
JDKAPI中Set接口的实现类有:
8.2 Set接口的常用方法
和List接口一样,Set接口也是Collection的子接口,因此,常用方法和Collection接口一样;
8.3 Set接口的遍历方式
同Collection的遍历方式一样,因为Set接口是Collection接口的子接口;
-
可以使用迭代器
-
增强for
-
不能使用索引的方式获取
public class Setmethod {
public static void main(String[] args) {
//1. 以Set接口的实现类,HashSet来讲解Set接口的方法
//2. set接口的实现类的对象(Set接口对象),不能存放重复的元素,可以添加一个null
//3. set接口对象存放数据是无序的(即添加的顺序和取出的顺序不一致)
//4. 注意:取出的顺序虽然不是添加的顺序,但是他是固定的;
Set set = new HashSet();
Set set2 = new HashSet();
set.add("john");
set.add("lucy");
set.add("mary");
set.add("jack");
set.add(null);
set.add(null);
for (int i = 0; i < 3; i++) {
System.out.println("set=" + set);
}
//删除
set.remove(null);
boolean flag = set.equals(5);
System.out.println(flag);
//遍历
//方式1: 使用迭代器
System.out.println("=========使用迭代器=========");
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
System.out.println("obj=" + obj);
}
//方式2: 使用增强For
System.out.println("=========使用增强for=========");
for (Object o : set) {
System.out.println("o=" + o);
}
//set接口对象,不能通过索引获取
}
}
9. Set接口实现类-HashSet
9.1HashSet的全面说明
-
HashSet实现了Set接口;
-
HashSet实际上是HashMap,看下源码;
public HashSet() {
map = new HashMap<>();
}
-
可以存放null值,但是只能有一个null;
-
HashSet不保证元素是有序,取决于hash后,在确定索引的结果;(不保证存放元素的顺序和取出顺序一致);
-
不能有重复元素/对象,在Set接口使用已经讲过;
9.2 HashSet底层机制说明
分析HashSet底层是HashMap,HashMap底层是(数组+链表+红黑树);
为了方便理解,模拟一个简单的数组+链表结构;
集合
1. 集合的理解和好处
-
数组
-
长度开始时必须指定,而且一旦指定,不能更改;
-
保存的必须为同一类型的元素
-
使用数组进行增加元素的示意代码-----比较麻烦
-
集合
-
可以动态保存任意多个对象,使用比较方便
-
提供了一系列方便的操作对象:add,remove,set,get等
-
使用集合添加,删除新元素的示意代码----简洁了
2. 集合的框架体系
java的集合类很多,主要分为两大类,如图:
-
集合主要是两组(单列集合,双列集合);
-
Collection接口有两个重要的子接口,List,Set,他们的实现子类都是单列集合
-
Map接口的实现子类 是双列集合,存放的K-V
3. Collection接口和常用方法
3.1 Collection接口实现类的特点
public interface Collection<E> extends Iterable<E>
-
collection实现子类可以存放多个元素,每个元素可以是object;
-
有些Collection的实现类,可以存放重复的元素,有些不可以;
-
有些Collection的实现类,有些是有序的(List),有些不是有序的(Set);
-
Collection接口没有直接的实现类,是通过它的子接口Set和List来实现的;
3.2 Collection接口和常用方法
Collection接口常用方法,以实现子类ArrayList来演示.
-
add:添加单个元素
-
remove:删除指定元素
-
contains:查找元素是否存在
-
size:获取元素个数
-
isEmpty:判断是否为空
-
clear:清空
-
addAll:添加多个元素
-
containsAll:查找多个元素是否都存在
-
removeAll:删除多个元素
public class CollectionMethod {
public static void main(String[] args) {
//add:添加单个元素
List list = new ArrayList();
list.add("jack");
list.add(10);//list.add(new Integer(10)) //自动装箱拆箱
list.add(true);
System.out.println("list="+list);
//remove:删除单个元素
// list.remove(0);//删除第一个元素
list.remove(true);//自动装箱拆箱
// System.out.println("list="+list);
//contains:查找摸个元素是否存在
System.out.println(list.contains("jack"));
//size:获取元素个数
System.out.println(list.size());
//isEmpty:判断是否为空
System.out.println(list.isEmpty());
//clear:清空
list.clear();
System.out.println("list="+list);
//addAll:添加多个元素
ArrayList list2 = new ArrayList();
list2.add("红楼梦");
list2.add("西游记");
list2.add("三国演义");
list2.add("水浒传");
list.addAll(list2);
System.out.println("list="+list);
//containsAll:查找多个元素是否存在
System.out.println(list.containsAll(list2));
//removeALL:删除多个元素
ArrayList list3 = new ArrayList();
list3.add("水浒传");
list3.add("三国演义");
list.removeAll(list3);
System.out.println("list="+list);
}
}
3.3 Colletion接口遍历方式1-使用Iterator(迭代器)
1. 基本介绍
-
Iterator对象称为迭代器,主要用于遍历Collection集合中的元素
-
所有实现了Collection接口的集合类都有一个Iterator()方法,用于返回一个实现了Iterator接口的对象,即可以
返回一个迭代器
-
Iterator的结构
-
Iterator仅用于遍历集合,Iterator本身并不存放对象;
2. 迭代器的执行原理
-
当创建完成指向某个集合或者容器的Iterator对象是,这是的指针其实指向的是第一个元素的上方,即指向一个 空
-
当调用hasNext方法的时候,只是判断下一个元素的有无,并不移动指针
-
当调用next方法的时候,向下移动指针,并且返回指针指向的元素,如果指针指向的内存中没有元素,会报异常。
-
remove方法删除的元素是指针指向的元素。如果当前指针指向的内存中没有元素,那么会抛出异常。
Iterator iterator = coll.iterator();//得到一个集合的迭代器
//快捷键itit:快速遍历迭代器
while (iterator.hasNext()){//判断是否还有下一个元素
Object obj = iterator.next();//1.指针下移 2.将下移以后集合位置上的元素返回
System.out.println(obj);
}
3. Iterator接口的方法
hasNext:没有指针下移操作,只是判断是否存在下一个元素 next:指针下移,返回该指针所指向的元素 remove:删除当前指针所指向的元素,一般和next方法一起用,这时候的作用就是删除next方法返回的元素
注意:在调用iterator.next()方法之前必须要调用iterator.hasNext()进行检测.若不调用,且下一条记录无效,直接调用iterator.next()会抛出NoSuchElementException异常;
public class CollectionIterator {
public static void main(String[] args) {
Collection coll = new ArrayList();
coll.add(new Book("小李飞刀", "古龙", 5.1));
coll.add(new Book("红楼梦", "曹雪芹", 34.6));
coll.add(new Book("三国演义", "罗贯中", 10.1));
//System.out.println(collection);
//先得到coll对应的迭代器
Iterator iterator = coll.iterator();
while (iterator.hasNext()){//判断是否还有下一个元素
//返回下一个元素,类型是Object
Object obj = iterator.next();
System.out.println(obj);
}
//当退出while循环后,这时Iterator迭代器,指向最后的元素
//iterator.next();//NoSuchElementException
//如果希望再次遍历,需要重置我们的迭代器
iterator = coll.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
}
}
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;
}
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", author='" + author + '\'' +
", 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;
}
}
3.4 Collection接口遍历对象方式2-for循环增强
增强for循环,可以代替Iterator迭代器,特点:增强for就是简化版的Iterator,本质一样,只能用于遍历集合或数组;
1. 基本语法
for(元素类型 元素名 : 集合名或数组名){
访问元素
}
2.底层原理
-
使用增强For,在Collection集合
-
增强for,底层仍然是迭代器
-
增强for可以理解成就是简化版本的迭代器遍历
-
快捷方式 I 或者 集合对象.for
-
增强for也可以直接在数组中使用
public class CollectionFor {
public static void main(String[] args) {
Collection coll = new ArrayList();
coll.add(new Book("小李飞刀", "古龙", 5.1));
coll.add(new Book("红楼梦", "曹雪芹", 34.6));
coll.add(new Book("三国演义", "罗贯中", 10.1));
//1.使用增强For,在Collection集合
//2.增强for,底层仍然是迭代器
//3.增强for可以理解成就是简化版本的迭代器遍历
//4.快捷方式 I 或者 集合对象.for
for(Object book:coll){
System.out.println("book"+book);
}
//增强for也可以直接在数组中使用
int[] nums = {1,8,10,20};
for(int i:nums){
System.out.println(i);
}
}
}
4. List接口和常用方法
4.1 List接口基本介绍
List接口是Collection接口的子接口
-
List集合中元素有序(即添加顺序和取出顺序一致),且可重复;
-
List集合中的每个元素都有器对应的顺序索引,即支持索引;
-
List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素
-
JDK API中List接口的实现类有很多,常用的有:ArrayList,LinkedList,Vector;
4.2 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 fromIndex,int toIndex):返回从fromIndex到toIndex位置的子集合; //注意:返回的子集合 包左不包右 fromIndex <= subList < toIndex
public class ListMethod {
@SuppressWarnings("all")
public static void main(String[] args) {
List list = new ArrayList();
list.add("张三丰");
list.add("贾宝玉");
list.add("张三");
//1.void add(int index,Object ele):在index位置插入ele元素
//在index = 1的位置插入一个对象
list.add(1,"张三");
System.out.println("list="+list);
System.out.println("----------------");
//2.boolean addAll(int index,Collection eles):从index位置开始将eles中的所有元素添加进来
List list2 = new ArrayList();
list2.add("jack");
list2.add("tom");
list.addAll(1,list2);
System.out.println("list="+list);
System.out.println("----------------");
//3.Object get(int index):获取指定index位置的元素
//4.int indexOf(Object obj):返回obj在集合中首次出现的位置
System.out.println(list.indexOf("tom"));//2
System.out.println("----------------");
//5.int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
System.out.println(list);
System.out.println(list.lastIndexOf("张三"));//5
System.out.println("----------------");
//6. Object remove(int index):移除指定index位置的元素,并返回此元素
list.remove(0);
System.out.println("remove:"+list);
System.out.println("----------------");
//7.Object set(int index,Object ele):设置指定index位置的元素为ele,相当于是替换
list.set(1,"阿狸");
System.out.println(list);
System.out.println("----------------");
//8.List subList(int fromIndex,int toIndex):返回从fromIndex到toIndex位置的子集合
//注意:返回的子集合 包左不包右 fromIndex <= subList < toIndex
System.out.println(list);
List resList = list.subList(1,list.size());
System.out.println(resList);
}
}
4.3 List的三种遍历方式
List接口实现子类[ArrayList,LinkedList,Vector],遍历方式相同
-
方式一:使用Iterator
Iterator iter = list.Iterator();
while(iter.hasNext()){
• Object 0 = iter.next();
}
-
方式二:使用增强for
for(Object o : list){
}
-
方式三:使用普通for
for (int i = 0; i < list.size(); i++) {
Object obj = list.get(i);
System.out.println(obj);
}
说明:使用LinkedList完成使用方式和ArrayList一样
5. ArrayList底层结构和源码分析
5.1 ArrayList的注意事项
-
permits all elements,including null;(允许所有元素,包括null),ArrayList可以加入null,并且多个;
-
ArrayList是由数组来实现数据存储的
-
ArrayList基本等同于Vector,除了ArrayList是线程不安全(执行效率高)看源码,没有synchronized关键字修饰
在多线程情况下,不建议使用ArrayList;
5.2 ArrayList的底层操作机制源码分析(重点,难点)
-
ArrayList中维护了一个Object类型的elementData数组
transient Object[] elementData;//transient 表示瞬间,短暂的,表示该属性不会被序列化
(Java序列化是指把Java对象转换为字节序列的过程,而Java反序列化是指把字节序列恢复为Java对象的过程)
-
当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第一次添加,则扩容elementData为10,则需要再次扩容,则扩容elementData为1.5倍;
-
如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍;
6. Vector底层结构和源码剖析
6.1 Vector的基本介绍
-
Vector类的定义说明;
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
-
Vector底层也是一个对象数组;
protected Object[] elementData;
-
Vector是线程同步,即线程安全,Vector类的操作方法带有synchronized;
public synchronized E get(int index) {
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
return elementData(index);
}
-
在开发中,需要线程同步安全时,考虑使用Vector;
6.2 Vector和ArrayList的比较
6.3 Vector底层结构
public class Vector_ {
public static void main(String[] args) {
//无参构造器
//Vector vector = new Vector();
Vector vector = new Vector(10);
for (int i = 0; i < 10; i++) {
vector.add(i);
}
vector.add(100);
System.out.println("vector="+vector);
//解读源码
/*
//1. new Vector() 底层
public Vector() {
this(10);
}
补充:如果是有参数构造 Vector vector = new Vector(8);
走的方法是:
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
2.vector.add(i)
2.1 //下面这个方法就添加数据到vector集合
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
2.2 //确定是否需要扩容 条件 : minCapacity - elementData.length > 0
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
2.3 //如果需要的数组大小 不够用,就扩容 ,扩容的算法
//newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
//就是扩容两倍
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
*/
}
}
7. LinkedList底层结构
7.1 LinkedList的全面说明
-
LinkedList底层实现了双向链表和双端队列特点;
-
可以添加任意元素(元素可以重复,包括null);
-
线程不安全,没有实现同步;
7.2LinkedList的底层操作机制
-
LinkedList底层维护了一个双向链表;
-
LinkedList中维护了两个属性first和last分别指向首节点和尾结点;
-
每个节点(Node对象),里面又维护了prev,next,item三个属性,其中通过prev指向前一个,通过next指向后一个节点,最终实现双向链表;
-
所以LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高;
-
模拟一个简单的双向链表;
public class LinkedList01 {
public static void main(String[] args) {
//模拟一个简单的双向链表
Node jack = new Node("jack");
Node tom = new Node("tom");
Node mary = new Node("mary");
//连接三个节点,形成双向链表
//jack -> tom -> mary
jack.next = tom;
tom.next = mary;
//mary -> tom -> jack
mary.pre = tom;
tom.pre = jack;
Node first = jack;//让first引用指向jack,就是双向链表的头结点
Node last = mary;//让last引用指向mary,就是双向链表的尾结点
//演示 , 从头到尾遍历
System.out.println("从头到尾遍历===================");
while (true) {
if (first == null) {
break;
}
System.out.println(first);
first = first.next;
}
//演示 , 从尾到头遍历
System.out.println("从尾到头遍历===================");
while (true) {
if (last == null) {
break;
}
System.out.println(last);
last = last.pre;
}
//演示链表的添加对象/数据,是多么方便
//要求,在tom 和 mary之间 插入一个对象 smith
//1.先创建一个Node结点,name就是smith
Node smith = new Node("smith");
//下面就把smith加入双向链表了
tom.next = smith;
smith.pre = tom;
smith.next = mary;
mary.pre=smith;
System.out.println("===========================");
//让fist再次指向jack
first = jack;//重置一下first
while (true) {
if (first == null) {
break;
}
System.out.println(first);
first = first.next;
}
}
}
//定义一个Node类,Node对象 表示双向链表的一个节点
class Node {
public Object item;//真正存放数据
public Node next;//指向后一个节点
public Node pre;//指向前一个节点
public Node(Object item) {
this.item = item;
}
@Override
public String toString() {
return "Node name=" + item;
}
}
7.3 LinkedList底层结构
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();//这里默认删除的是第一个结点
//linkedList.remove(2);
System.out.println("LinkedList="+linkedList);
//修改某个结点
linkedList.set(1,999);
System.out.println("LinkedList="+linkedList);
//得到某个结点对象
//get(1)是得到双向链表的第二个对象
Object o = linkedList.get(1);
System.out.println(o);//999
//因为LinkedList是实现了List接口 ,遍历方式
System.out.println("====LinkedList遍历迭代器====");
Iterator iterator = linkedList.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
System.out.println("====LinkedList遍历增强For====");
for (Object oo :linkedList) {
System.out.println(oo);
}
System.out.println("====LinkedList遍历普通for====");
for (int i = 0; i < linkedList.size(); i++) {
System.out.println(linkedList.get(i));
}
//源码阅读
/*
1. LinkedList linkedList = new LinkedList();
public LinkedList(){}
2.这时LinkedList的属性:first = null last=null
3.执行添加方法
public boolean add(E e) {
linkLast(e);
return true;
}
4.将新的结点,加入到双向链表的最后;
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) {
// assert f == first && f != null;
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null; // help GC
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
*/
}
}
7.4 ArrayList和LinkedList的比较
-
ArrayList和LinkedList的比较
ArrayList改查的效率较高,因为可以通过索引直接定位;LinkedList改查效率较低,会从头开始,一个一个遍历;
-
如何选择ArrayList和LinkedList:
-
如果我们改查的操作多,选择ArrayList;
-
如果我们增删的操作多,选择LinkedList;
-
一般来说,在程序中,80%-90%都是查询,因此大多数情况下会选择ArrayList;
-
在一个项目中,根据业务灵活选择,也可以这样,一个模块下使用的是ArrayList,另一个模块下选择的是LinkedList,也就是说,要根据业务来进行选择;
8. Set接口和常用方法
8.1 Set接口基本介绍
-
无序(添加和取出顺序不一致),没有索引;
-
不允许重复元素,所以最多包含一个null;
-
JDKAPI中Set接口的实现类有:
8.2 Set接口的常用方法
和List接口一样,Set接口也是Collection的子接口,因此,常用方法和Collection接口一样;
8.3 Set接口的遍历方式
同Collection的遍历方式一样,因为Set接口是Collection接口的子接口;
-
可以使用迭代器
-
增强for
-
不能使用索引的方式获取
public class Setmethod {
public static void main(String[] args) {
//1. 以Set接口的实现类,HashSet来讲解Set接口的方法
//2. set接口的实现类的对象(Set接口对象),不能存放重复的元素,可以添加一个null
//3. set接口对象存放数据是无序的(即添加的顺序和取出的顺序不一致)
//4. 注意:取出的顺序虽然不是添加的顺序,但是他是固定的;
Set set = new HashSet();
Set set2 = new HashSet();
set.add("john");
set.add("lucy");
set.add("mary");
set.add("jack");
set.add(null);
set.add(null);
for (int i = 0; i < 3; i++) {
System.out.println("set=" + set);
}
//删除
set.remove(null);
boolean flag = set.equals(5);
System.out.println(flag);
//遍历
//方式1: 使用迭代器
System.out.println("=========使用迭代器=========");
Iterator iterator = set.iterator();
while (iterator.hasNext()) {
Object obj = iterator.next();
System.out.println("obj=" + obj);
}
//方式2: 使用增强For
System.out.println("=========使用增强for=========");
for (Object o : set) {
System.out.println("o=" + o);
}
//set接口对象,不能通过索引获取
}
}
9. Set接口实现类-HashSet
9.1HashSet的全面说明
-
HashSet实现了Set接口;
-
HashSet实际上是HashMap,看下源码;
public HashSet() {
map = new HashMap<>();
}
-
可以存放null值,但是只能有一个null;
-
HashSet不保证元素是有序,取决于hash后,在确定索引的结果;(不保证存放元素的顺序和取出顺序一致);
-
不能有重复元素/对象,在Set接口使用已经讲过;
9.2 HashSet底层机制说明
分析HashSet底层是HashMap,HashMap底层是(数组+链表+红黑树);
为了方便理解,模拟一个简单的数组+链表结构;
标签:list,System,println,add,集合,public,out From: https://www.cnblogs.com/wangzheng888/p/16858493.html