学习JavaEE的日子 Day23 迭代器,LinkedList,Vector,Stack,HashSet,LinkedHashSet

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:
数据结构:一维数组+单向链表+双向链表
特点:有序+去重
注意:理解为什么有序,为什么去重

简答题

1.ArrayList与Vector区别

Vector类的所有方法都是同步的。可以由两个线程安全地访问一个Vector对象、但是一个线程访问Vector的话代码要在同步操作上耗费大量的时间。

Arraylist不是同步的,所以在不需要保证线程安全时时建议使用Arraylist。

2.如何实现数组和 List 之间的转换?

数组转 List:使用 Arrays. asList(array) 进行转换。

List 转数组:使用 List 自带的 toArray() 方法。

2.说一下 HashSet 的实现原理?

HashSet 是基于 HashMap 实现的,HashSet的值存放于HashMap的key上,因此 HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,HashSet 不允许重复的值。

3.HashSet如何检查重复?HashSet是如何保证数据不可重复的?

向HashSet 中add ()元素时,判断元素是否存在的依据,不仅要比较hash值,同时还要结合equles 方法比较。 HashSet 中的add ()方法会使用HashMap 的put()方法。

HashMap 的 key 是唯一的,由源码可以看出 HashSet 添加进去的值就是作为HashMap的key,并且在HashMap中如果K/V相同时,会用新的V覆盖掉旧的V,然后返回旧的V。所以不会重复( HashMap 比较key是否相等是先比较hashcode 再比较equals )。

5.HashSet与HashMap的区别

HashMapHashSet
实现了Map接口实现Set接口
存储键值对仅存储对象
调用 put()向map中添加元素调用 add()方法向Set中添加元素
HashMap使用键 (Key)计算HashcodeHashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode 可能相同,所以 equals()方 法用来判 断对象的相等性, 如果两个对象不同的话,那么返回false
HashMap 相对于HashSet 较快,因为它是使用唯一的键获取对象HashSet较HashMap来说比较慢

HashMap和HashSet有什么区别?
HashMap是一个基于哈希表的Map接口实现,它存储键值对。而HashSet是一个基于HashMap实现的Set接口,它存储的是不重复的元素。在HashSet内部,它实际上是使用HashMap来存储元素的,每个元素都作为键存储在HashMap中,值则是一个固定的Object对象。

总结

撕迭代器底层

LinkedList:
和ArrayList使用一致
栈模式、队列模式

Vector:
和ArrayList的数据结构一致(一维数组),但是Vector是线程安全的(加锁)
Vector有老的方法

Stack:
Vector的子类
栈模式

HashSet:
数据结构:一维数组+单向链表
特点:无序+去重
注意:理解为什么无序,为什么去重

LinkedHashSet:
数据结构:一维数组+单向链表+双向链表
特点:有序+去重
注意:理解为什么有序,为什么去重

  • 47
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

A 北枝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值