Day23
1.迭代器
含义:遍历集合中的数据
分类:Iterator 和 ListIterator
Iterator 和 ListIterator 区别
Iterator :Collection接口下所有的实现类都可以获取的迭代器,可以在遍历时删除元素
ListIterator :List接口下所有的实现类可以获取的迭代器,可以在遍历时删除、替换、添加元素,也可以指定下标开始遍历,还可以倒叙遍历
1.1 研究foreach/增强for循环
注意:foreach底层是由Iterator实现
public class Test01 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
for (String element : list) {
System.out.println(element);
}
//foreach遍历集合的底层实现:
// String element;
// for(Iterator it = list.iterator();it.hasNext();System.out.println(element))
// element = (String)it.next();
}
}
注意:
1.泛型只在编码阶段有效,不会编译到class文件2.泛型只支持引用数据类型
1.2 研究Iterator遍历ArrayList集合的底层原理
注意:
研究底层原理,就势必会看底层源码
底层源码必须找场景去研究,不能从上往下去理解(会越看越乱码)
public class Test02 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String element = it.next();
System.out.println(element);
}
}
}
思考题:为什么添加和删除才会把modCount++? 为什么修改和查询不会把modCount++?
分析:
集合用于存储数据、管理数据 – 增删改查
进行添加或删除操作时,会改变集合的元素个数
size相等于指针移动,modCount是记录操作(添加和删除)的次数
ArrayList添加元素的过程:
1.判断是否扩容(modCount++)
2.将元素添加到一维数组指定下标上的位置
面试官:ArrayList的数据结构是什么?
一维数组的容器
问你的是元素是存在ArrayList哪里的
为什么研究迭代器的底层???
1.让大家更深入理解面相对象程序设计的思想
2.应付面试
研究源码,必须找场景!!!
//场景
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String element = it.next();
System.out.println(element);
}
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
//外部操作数
//作用:记录修改元素的次数(添加、删除会让该变量++)
protected transient int modCount = 0;//modCount - 5
}
public class ArrayList<E> extends AbstractList<E> implements List<E>{
//元素个数
private int size;//size - 4
//数据容器 - ["aaa","bbb","ccc","ddd",null,null,null,null,null,null]
transient Object[] elementData;
//e - ddd
public boolean add(E e) {
ensureCapacityInternal(size + 1);//判断是否扩容
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
public Iterator<E> iterator() {
return new Itr();
}
//ArrayList类的内部类 -- 实现了遍历元素的功能
private class Itr implements Iterator<E> {
int cursor; // 游标 - 4
int lastRet = -1; // 当前元素的下标 - 3
int expectedModCount = modCount;//内部操作数 - 5
public boolean hasNext() {
return cursor != size;//4 - 4
}
@SuppressWarnings("unchecked")
public E next() {
/**
思考题:为什么在获取元素时,会先判断外部操作数是否等于内部操作数
考虑到遍历元素时,如果添加或删除元素,会导致数据个数变化
遍历时就有可能出现脏数据,如果外部操作数和内部操作数不相同就以为数据不同意,就报错!
*/
checkForComodification();//判断外部操作数是否等于内部操作数,如果不等于就报错
int i = cursor;//i - 3
if (i >= size)
throw new NoSuchElementException();
//获取外部类的成员属性 -- elementData
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
//elementData -- 获取的是ArrayList(外部类的)elementData -> Object[] elementData;
//Object类型的元素强转为集合中真实类型的元素
// --> elementData[3] --> Object类型的数据需要强转为String类型
return (E) elementData[lastRet = i];//elementData[3]
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
//重写了Iterator接口中的remove()
public void remove() {
//判断当前元素的下标是否小于0
if (lastRet < 0)
throw new IllegalStateException();
//判断外部操作数是否和内部操作数一致,不一致就会报错
checkForComodification();
try {
//利用ArraList类的remove()去删除元素
ArrayList.this.remove(lastRet);
//把当前元素的下标赋值给游标
cursor = lastRet;
//把-1赋值给lastRet
lastRet = -1;
//重新把外部操作数赋值给内部操作数
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
}
思考题:为什么迭代器是一个接口,不是类???
因为Java提供了很多的集合,不同的集合实现增删改查的原理是不一样的,
所以,不同的集合都实现了各自遍历元素的代码(实现了各自的迭代器)
//迭代器的接口
public interface Iterator<E> {
//判断是否有可迭代的元素
boolean hasNext();
//获取下一个元素
E next();
//删除元素的默认方法(作用:给实现类去重写,如果实现类可以选择不重写,意味着遍历时不能删除元素,当然实现类可以选择重写,那么就意味着这个迭代器可以在遍历元素时删除元素)
default void remove() {
//抛出异常 -- 删除异常
throw new UnsupportedOperationException("remove");
}
}
需求:使用Iterator遍历元素,遍历到“bbb”时删除该元素
public class Test03 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String element = it.next();
if(element.equals("bbb")){
//注意:在Iterator遍历时,不能使用list.remove(),会报错
//list.remove(element);
//注意:在Iterator遍历时,如果要删除元素,使用迭代器中的remove()
//思路:1.告诉ArrayList需要删除的元素 2.迭代器内部在更新现有数据
it.remove();
}
}
for (String element : list) {
System.out.println(element);
}
}
}
1.3 研究ListIterator遍历
需求:使用ListIterator遍历元素
public class Test04 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
ListIterator<String> listIterator = list.listIterator();
while(listIterator.hasNext()){
String element = listIterator.next();
System.out.println(element);
}
}
}
底层源码:
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
//外部操作数
//作用:记录修改元素的次数(添加、删除会让该变量++)
protected transient int modCount = 0;//modCount - 5
}
public class ArrayList<E> extends AbstractList<E> implements List<E>{
//元素个数
private int size;//size - 4
//数据容器 - ["aaa","bbb","ccc","ddd",null,null,null,null,null,null]
transient Object[] elementData;
//e - ddd
public boolean add(E e) {
ensureCapacityInternal(size + 1);//判断是否扩容
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
public Iterator<E> iterator() {
return new Itr();
}
//ArrayList类的内部类 -- 实现了遍历元素的功能
private class Itr implements Iterator<E> {
int cursor; // 游标 - 4
int lastRet = -1; // 当前元素的下标 - 3
int expectedModCount = modCount;//内部操作数 - 5
public boolean hasNext() {
return cursor != size;//4 - 4
}
@SuppressWarnings("unchecked")
public E next() {
/**
思考题:为什么在获取元素时,会先判断外部操作数是否等于内部操作数
考虑到遍历元素时,如果添加或删除元素,会导致数据个数变化
遍历时就有可能出现脏数据,如果外部操作数和内部操作数不相同就以为数据不同意,就报错!
*/
checkForComodification();//判断外部操作数是否等于内部操作数,如果不等于就报错
int i = cursor;//i - 3
if (i >= size)
throw new NoSuchElementException();
//获取外部类的成员属性 -- elementData
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
//elementData -- 获取的是ArrayList(外部类的)elementData -> Object[] elementData;
//Object类型的元素强转为集合中真实类型的元素
// --> elementData[3] --> Object类型的数据需要强转为String类型
return (E) elementData[lastRet = i];//elementData[3]
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
//重写了Iterator接口中的remove()
public void remove() {
//判断当前元素的下标是否小于0
if (lastRet < 0)
throw new IllegalStateException();
//判断外部操作数是否和内部操作数一致,不一致就会报错
checkForComodification();
try {
//利用ArraList类的remove()去删除元素
ArrayList.this.remove(lastRet);
//把当前元素的下标赋值给游标
cursor = lastRet;
//把-1赋值给lastRet
lastRet = -1;
//重新把外部操作数赋值给内部操作数
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
public ListIterator<E> listIterator() {
return new ListItr(0);//0 - 设置游标的位置
}
//指定游标位置
public ListIterator<E> listIterator(int index) {
return new ListItr(index);
}
private class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) {
super();
cursor = index;
}
//判断是否有上一个元素
public boolean hasPrevious() {
return cursor != 0;
}
//获取下一个元素的下标
public int nextIndex() {
return cursor;
}
//获取上一个元素的下标
public int previousIndex() {
return cursor - 1;
}
//获取上一个元素
@SuppressWarnings("unchecked")
public E previous() {
checkForComodification();
int i = cursor - 1;
if (i < 0)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i;
return (E) elementData[lastRet = i];
}
//设置元素到当前下标上
public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
//ListIterator的set功能还是依赖于ArrayList
ArrayList.this.set(lastRet, e);
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
//添加元素到当前下标上
public void add(E e) {
checkForComodification();
try {
int i = cursor;
//ListIterator的add功能还是依赖于ArrayList
ArrayList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
}
需求:使用ListIterator遍历元素,遍历到“bbb”时删除该元素
public class Test05 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
ListIterator<String> listIterator = list.listIterator();
while(listIterator.hasNext()){
String element = listIterator.next();
if(element.equals("bbb")){
listIterator.remove();
}
}
for (String element : list) {
System.out.println(element);
}
}
}
需求:使用ListIterator遍历元素,遍历到“bbb”时替换该元素
public class Test06 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
ListIterator<String> listIterator = list.listIterator();
while(listIterator.hasNext()){
String element = listIterator.next();
if(element.equals("bbb")){
listIterator.set("王益升");
}
}
for (String element : list) {
System.out.println(element);
}
}
}
需求:使用ListIterator遍历元素,遍历到“bbb”时添加元素
public class Test07 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
ListIterator<String> listIterator = list.listIterator();
while(listIterator.hasNext()){
String element = listIterator.next();
if(element.equals("bbb")){
listIterator.add("王益升");
}
}
for (String element : list) {
System.out.println(element);
}
}
}
需求:使用ListIterator指定下标遍历元素
public class Test08 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
ListIterator<String> listIterator = list.listIterator(2);
while(listIterator.hasNext()){
String element = listIterator.next();
System.out.println(element);
}
}
}
需求:使用ListIterator倒序遍历元素
public class Test09 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
ListIterator<String> listIterator = list.listIterator(list.size());//把指针移动到最后
while(listIterator.hasPrevious()){//判断是否有上一个元素
String element = listIterator.previous();//获取上一个元素
System.out.println(element);
}
}
}
小结
Iterator:
理解:Collection接口下所有的实现类都能获取的迭代器
作用:
遍历元素
删除元素
ListIterator:
理解:List接口下所有的实现类才能获取的迭代器
注意:ListIterator继承Iterator
作用:
遍历元素
删除元素
替换元素
添加元素
指定下标开始遍历
倒序遍历
2.LinkedList(双向链表)
2.1 LinkedList的使用
注意:
LinkedList和ArrayList使用是一模一样的,这要归功于Conllection和List接口
因为接口是定义标准的!!!
只不过LinkedList和ArrayList底层实现原理是不一样的,LinkedList底层使用双向链表去实现对于数据的操作(增删改查)
public class Test01 {
public static void main(String[] args) {
//创建LinkedList集合的对象
LinkedList<String> list = new LinkedList<>();
//添加元素
list.add("小希");
list.add("小空");
list.add("小丽");
list.add("小光");
list.add("小李");
list.add("小阳");
list.add("小川");
//设置指定下标上的元素
list.set(1, "小王");
//获取指定下标上的元素
String str = list.get(1);
System.out.println("获取指定下标上的元素:" + str);//小王
//获取元素个数
int size = list.size();
System.out.println("获取元素个数:" + size);//7
//将元素添加到指定下标的位置
list.add(4, "小北");
LinkedList<String> newList1 = new LinkedList<>();
Collections.addAll(newList1, "aaa","bbb","ccc","ccc");//利用Collections工具类给集合做批量添加
list.addAll(newList1);//将newList1中所有的元素添加到list集合的末尾
LinkedList<String> newList2 = new LinkedList<>();
Collections.addAll(newList2, "xxx","yyy","zzz","zzz");//利用Collections工具类给集合做批量添加
list.addAll(4, newList2);//将newList2中所有的元素添加到list集合指定下标的位置
//清空集合中所有的元素
//list.clear();
System.out.println("判断集合中是否包含指定元素:" + list.contains("王益升"));//true
System.out.println("判断集合中是否包含子集合中所有的元素:" + list.containsAll(newList1));//true
System.out.println("获取集合中第一次出现该元素的下标:" + list.indexOf("ccc"));//13
System.out.println("获取集合中最后一次出现该元素的下标:" + list.lastIndexOf("ccc"));//14
System.out.println("判断集合中是否没有元素:" + list.isEmpty());//false
list.remove(0);//根据下标删除元素
list.remove("小光");//根据元素删除元素
list.removeAll(newList1);//删除list中包含newList1的元素(去除交集)
list.retainAll(newList2);//保留list中包含newList2的元素(保留交集)
List<String> subList = list.subList(1, 3);//截取开始下标(包含)到结束下标(排他)处的元素,返回新的List集合
//将集合转成数组
Object[] objs = subList.toArray();
System.out.println(Arrays.toString(objs));
//将集合转成数组
String[] ss = new String[2];
subList.toArray(ss);
System.out.println(Arrays.toString(ss));
System.out.println("--------------------------------");
//遍历数据 -- for循环
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
System.out.println("--------------------------------");
//遍历数据 -- foreach
for (String element : list) {
System.out.println(element);
}
System.out.println("--------------------------------");
//遍历数据 -- Iterator
Iterator<String> it = list.iterator();
while(it.hasNext()){//判断是否有可迭代的元素
String element = it.next();//返回下一个元素
System.out.println(element);
}
System.out.println("--------------------------------");
//遍历数据 -- ListIterator
ListIterator<String> listIterator = list.listIterator();
while(listIterator.hasNext()){//判断是否有可迭代的元素
String element = listIterator.next();//返回下一个元素
System.out.println(element);
}
}
}
2.2 LinkedList独有的方法
public class Test02 {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
list.add("小希");
list.add("abc");
list.add("小空");
list.add("abc");
list.add("小丽");
list.add("abc");
list.add("小光");
//将元素添加到首部
list.addFirst("小王");
list.offerFirst("小新");
list.push("小杨");
//将元素添加到尾部
list.addLast("xxx");
list.offerLast("yyy");
list.offer("zzz");
System.out.println("获取第一个元素:" + list.element());
System.out.println("获取第一个元素:" + list.getFirst());
System.out.println("获取第一个元素:" + list.peek());
System.out.println("获取第一个元素:" + list.peekFirst());
System.out.println("获取最后一个元素:" + list.getLast());
System.out.println("获取最后一个元素:" + list.peekLast());
list.pop();//删除第一个元素
list.poll();//删除第一个元素
list.pollFirst();//删除第一个元素
list.removeFirst();
list.pollLast();//删除最后一个元素
list.removeLast();//删除最后一个元素
list.removeFirstOccurrence("abc");//删除第一次出现的元素
list.removeLastOccurrence("abc");//删除最后一个出现的元素
//倒序遍历
// Iterator<String> descendingIterator = list.descendingIterator();
// while(descendingIterator.hasNext()){//hasNext()底层是判断是否有上一个元素
// String element = descendingIterator.next();//next()底层是获取上一个元素
// System.out.println(element);
// }
System.out.println("----------------------------");
for (String element : list) {
System.out.println(element);
}
}
}
2.3 LinkedList 的 队列模式(先进先出)
public class Test03 {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
list.add("eee");
while(!list.isEmpty()){//判断是否没有元素
String element = list.removeFirst();//删除第一个元素,并返回
System.out.println(element);
}
System.out.println("获取元素个数:" + list.size());//0
}
}
2.4 LinkedList 的 栈模式(先进后出 或 后进先出)
public class Test04 {
public static void main(String[] args) {
LinkedList<String> list = new LinkedList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add("ddd");
list.add("eee");
while(!list.isEmpty()){
String element = list.removeLast();//删除最后一个元素,并返回
System.out.println(element);// eee ddd ccc bbb aaa
}
System.out.println("获取元素个数:" + list.size());//0
}
}
3.Vector(一维数组+线程安全)
3.1 Vector的使用
注意:
Vector和ArrayList使用是一模一样的,这要归功于Conllection和List接口
因为接口是定义标准的!!!
只不过Vector和ArrayList底层实现原理是不一样的,Vector底层使用一维数组+线程安全去实现对于数据的操作(增删改查)
3.2 研究Vector的老方法
Vector的历史:
Vector是元老级别的类(JDK1.0),集合框架是JDK1.2开始才推出的概念,
当时大部分程序员已经习惯Vector去存储数据,为了更好的推广集合框架和保留Vector,
Java让Vector又多实现了List接口
经验:老的方法大多数使用element这个单词
public class Test02 {
public static void main(String[] args) {
Vector<String> v = new Vector<>();
v.addElement("aaa");
v.addElement("bbb");
v.addElement("ccc");
v.addElement("ddd");
v.addElement("eee");
//清空
//v.removeAllElements();
v.removeElementAt(0);//根据下标删除元素
v.removeElement("ccc");//根据元素删除元素
v.setElementAt("小王", 1);//将元素设置到指定下标的位置
Enumeration<String> elements = v.elements();
while(elements.hasMoreElements()){
String element = elements.nextElement();
System.out.println(element);
}
}
}
4.Stack
4.1 研究Stack的特点 – 栈模式(先进后出)
注意:
该类继承Vector,所以Stack可以使用父类所有的非私有的方法
public class Test01 {
public static void main(String[] args) {
Stack<String> stack = new Stack<>();
//将元素添加到栈顶
stack.push("111");
stack.push("222");
stack.push("333");
stack.push("444");
stack.push("555");
System.out.println("获取栈顶元素:" + stack.peek());//555
int search = stack.search("222");
System.out.println("获取元素距离栈顶的个数(基于1):" + search);//4
//遍历取出元素
while(!stack.empty()){
//删除栈顶元素,并返回
String element = stack.pop();
System.out.println(element);//555 444 333 222 111
}
}
}
5.HashSet
5.1 HashSet的使用
注意:
HashSet实现Set接口,Set接口继承Collection接口
ArrayList实现List接口,List接口继承Collection接口
以为ArrayList里的方法除了针对于下标操作的,其余的都在Set接口中
public class Test01 {
public static void main(String[] args) {
//创建HashSet集合的对象
HashSet<String> set = new HashSet<>();
//添加元素
set.add("小希");
set.add("小空");
set.add("小丽");
set.add("小光");
set.add("小李");
set.add("小林");
set.add("小川");
set.add("xxx");
set.add("yyy");
set.add("zzz");
//获取元素个数
int size = set.size();
System.out.println("获取元素个数:" + size);//10
HashSet<String> newSet1 = new HashSet<>();
Collections.addAll(newSet1, "aaa","bbb","ccc","ccc");//利用Collections工具类给集合做批量添加
set.addAll(newSet1);//将newSet1中所有的元素添加到set集合的末尾
//清空集合中所有的元素
//set.clear();
System.out.println("判断集合中是否包含指定元素:" + set.contains("小王"));//true
System.out.println("判断集合中是否包含子集合中所有的元素:" + set.containsAll(newSet1));//true
System.out.println("判断集合中是否没有元素:" + set.isEmpty());//false
set.remove("小光");//根据元素删除元素
set.removeAll(newSet1);//删除set中包含newset1的元素(去除交集)
HashSet<String> newSet2 = new HashSet<>();
Collections.addAll(newSet2, "xxx","yyy","zzz","zzz");//利用Collections工具类给集合做批量添加
set.retainAll(newSet2);//保留set中包含newset2的元素(保留交集)
//将集合转成数组
Object[] objs = set.toArray();
System.out.println(Arrays.toString(objs));
//将集合转成数组
String[] ss = new String[set.size()];
set.toArray(ss);
System.out.println(Arrays.toString(ss));
System.out.println("--------------------------------");
//遍历数据 -- foreach
for (String element : set) {
System.out.println(element);
}
System.out.println("--------------------------------");
//遍历数据 -- Iterator
Iterator<String> it = set.iterator();
while(it.hasNext()){//判断是否有可迭代的元素
String element = it.next();//返回下一个元素
System.out.println(element);
}
}
}
5.1 HashSet的特点
特点:无序 + 去重
无序的原因:存入顺序的规则和取出顺序的规则不一致
去重的原因:元素的hash值相同,再判断hash&&==||equals,如果相同就不存入到集合中
public class Test02 {
public static void main(String[] args) {
HashSet<String> set = new HashSet<>();
set.add("aaa");
set.add("bbb");
set.add("ccc");
set.add("ddd");
set.add("ddd");
set.add("Aa");
set.add("BB");
for (String element : set) {
System.out.println(element);
}
}
}
存入顺序:
1.获取元素的hash值 – hashCode()
2.通过hash值计算在数组的下标
3.判断下标是否有元素
没有 – 直接添加元素
有 – 判断元素是否相同(hash && == || equals)
是 – 不存储(达到了去重的效果)
不是 – 添加元素(JDK1.7 头插法,JDK1.8 尾插法)
取出法:
遍历数组 + 单向链表
6.LinkedHashSet
6.1 LinkedHashSet的使用
注意:
LinkedHashSet 继承 HashSet
3.2 LinkedHashSet的特点
特点:有序 + 去重
有序的原因:在HashSet的基础上添加了双向链表
去重的原因:元素的hash值相同,再判断hash&&==||equals,如果相同就不存入到集合中
public class Test02 {
public static void main(String[] args) {
LinkedHashSet<String> set = new LinkedHashSet<>();
set.add("aaa");
set.add("bbb");
set.add("ccc");
set.add("ddd");
set.add("ddd");
set.add("Aa");
set.add("BB");
for (String element : set) {
System.out.println(element);
}
}
}
存入顺序:
1.获取元素的hash值 – hashCode()
2.通过hash值计算在数组的下标
3.判断下标是否有元素
没有 – 直接添加(记录上一个添加元素的地址,上一个元素会记录选择元素的地址,双向链表)
有 – 判断元素是否相同(hash && == || equals)
是 – 不添加(达到了去重的效果)
不是 – 添加元素(JDK1.7 头插法,JDK1.8 尾插法)
取出法:
1.找到第一个添加的元素
2.再向下找到下一个元素
注意
4.各种集合的应用场景
ArrayList:存数据,线程不安全
LinkedList:队列模式、栈模式,线程不安全
Vector:弃用,线程安全
Stack:弃用,线程安全
HashSet:去重+无序,线程不安全
LinkedHashSet:去重+有序,线程不安全
TreeSet:排序,线程不安全
HashMap:存key+value,key去重,无序,线程不安全
LinkedHashMap:存key+value,key去重,有序,线程不安全
Hashtable:弃用,存key+value,key去重,无序,线程安全,方法加锁-效率低
ConcurrentHashMap:存key+value,key去重,无序,线程安全,局部加锁、CAS-效率高
TreeMap:存key+value,针对于Key排序
Properties:配置文件
总结
撕迭代器底层
LinkedList:
和ArrayList使用一致
栈模式、队列模式
Vector:
和ArrayList的数据结构一致(一维数组),但是Vector是线程安全的(加锁)
Vector有老的方法
Stack:
Vector的子类
栈模式
HashSet:
数据结构:一维数组+单向链表
特点:无序+去重
注意:理解为什么无序,为什么去重
LinkedHashSet:
数据结构:一维数组+单向链表+双向链表
特点:有序+去重
注意:理解为什么有序,为什么去重
比较器接口
作用:排序时使用
分类:
内置比较器:Comparable - compareTo()
外置比较器:Comparator - compare()
使用场景:
内置比较器:对象要想存入TreeSet、TreeMap中,对象所属的类必须要实现内置比较器
外置比较器:当内置比较的规则不满足现在的需求,但又不能改动内置比较器规则时
优先级别:外置比较器 > 内置比较器
3.判断下标是否有元素
没有 – 直接添加(记录上一个添加元素的地址,上一个元素会记录选择元素的地址,双向链表)
有 – 判断元素是否相同(hash && == || equals)
是 – 不添加(达到了去重的效果)
不是 – 添加元素(JDK1.7 头插法,JDK1.8 尾插法)
取出法:
1.找到第一个添加的元素
2.再向下找到下一个元素
注意
4.各种集合的应用场景
ArrayList:存数据,线程不安全
LinkedList:队列模式、栈模式,线程不安全
Vector:弃用,线程安全
Stack:弃用,线程安全
HashSet:去重+无序,线程不安全
LinkedHashSet:去重+有序,线程不安全
TreeSet:排序,线程不安全
HashMap:存key+value,key去重,无序,线程不安全
LinkedHashMap:存key+value,key去重,有序,线程不安全
Hashtable:弃用,存key+value,key去重,无序,线程安全,方法加锁-效率低
ConcurrentHashMap:存key+value,key去重,无序,线程安全,局部加锁、CAS-效率高
TreeMap:存key+value,针对于Key排序
Properties:配置文件
总结
撕迭代器底层
LinkedList:
和ArrayList使用一致
栈模式、队列模式
Vector:
和ArrayList的数据结构一致(一维数组),但是Vector是线程安全的(加锁)
Vector有老的方法
Stack:
Vector的子类
栈模式
HashSet:
数据结构:一维数组+单向链表
特点:无序+去重
注意:理解为什么无序,为什么去重
LinkedHashSet:
数据结构:一维数组+单向链表+双向链表
特点:有序+去重
注意:理解为什么有序,为什么去重