5---迭代器+foreach+List接口和Set接口及它们的子类+哈希表

1、迭代器

1、什么是迭代:

​ 迭代是取数据的过程.先判断集合中是否有数据,如果有,就取出一个数据,接着再判断集合中是否有数据,
如果有再接着取出一个数据,这样往复循环直到所有数据都取出来了。

2、为什么要迭代器?

背景:List接口有索引,我们可以通过for循环+get方法来获取数据,但是Set接口这边没有索引,不能通过for循环+get方式获取数据。

​ 所以,Collection接口就创建了一种通用的方式方便所有的集合来获取数据。就是迭代器(Iterator)

3、迭代器和集合的关系:

​ 首先,Collection接口中有一个方法可以得到迭代器

interface Collection {
    Iterator<E> iterator() 返回此集合中元素的迭代器
}

​ 其次,举个例子:List接口继承了Collection集合,ArrayList类实现了List接口,也就也拥有了获取迭代器的方法

interface List extends Collection {
}

class ArrayList implements List {
    pulic Iterator<E> iterator() {
        return new 迭代器();
    }
}

​ 故,每个集合都实现iterator方法,都能获取到迭代器

4、如何获取迭代器?

Iterator <E> itr = 集合.iterator();

5、迭代器的常用方法:

​ (1)boolean hasNext()

​ 判断是否有下一个元素,如果有返回true
​ (2) E next()

​ 1.取出下一个元素
​ 2.将指针移动到下一个位置

public static void main(String[] args) {
    ArrayList<String> list = new ArrayList<>();
    list.add("马蓉");
    list.add("李小璐");
    list.add("白百合");
    list.add("张柏芝");
    Iterator<String> itr = list.iterator();
    // 2.循环判断是否有下一个元素
    while (itr.hasNext()) {
    // 3.如果有,就取出下一个元素
    System.out.println(itr.next());
    }

2、for循环的增强型写法(foreach)

1、格式:
for (数据类型 变量名 : 数组或集合) {

}

​ PS:常常快捷键:数组or集合名.for

2、优点:

​ 代码简单

3、缺点:

​ 没有索引

4、应用场合

​ 如果不关心索引就可以使用增强for(如Set集合)

5、增强for循环的底层

​ 通过反编译器可知,

​ 1.遍历数组,底层是普通for循环
​ 2.遍历集合,底层是迭代器

Collection集合分布:

在这里插入图片描述

1、List接口特点:

​ 1.有索引,存储和取出有顺序
​ 2.元素可以重复

2、List接口中的常用方法:

void add(int index, E element) 将指定的元素插入此列表中的指定位置(可选操作)。
​ **删 ** E remove(int index) 删除该列表中指定位置的元素(可选操作)。
E set(int index, E element) 用指定的元素(可选操作)替换此列表中指定位置的元素。

E get(int index) 返回此列表中指定位置的元素。

运用多态使用这些方法

public static void main(String[] args) {
    // 使用List接口的途径(多态)
    List<String> list = new ArrayList<>();

    list.add("钱大妈");
    list.add("老干妈");
    list.add("大姨妈");
    list.add("大姨妈");
    list.add("大姨妈");
    list.add("大姨妈");

    // void add(int index, E element) 将指定的元素插入此列表中的指定位置(可选操作)。
    list.add(0, "钱打野");

    System.out.println(list); // [钱打野, 钱大妈, 老干妈, 大姨妈]

    // E get(int index) 返回此列表中指定位置的元素。
    System.out.println(list.get(2));

    // E remove(int index) 删除该列表中指定位置的元素(可选操作)。
    System.out.println(list.remove(1)); // 被删除的元素
    System.out.println("删除后:" + list); // 删除后:[钱打野, 老干妈, 大姨妈]

    // E set(int index, E element) 用指定的元素(可选操作)替换此列表中指定位置的元素。
    list.set(1, "老干爹");
    System.out.println("修改后: " + list); // 修改后: [钱打野, 老干爹, 大姨妈]
}
3、List的其中一个子类:LinkedList集合

1、特点:

​ 因为底层结构是链表,所以查询慢,增删快

class LinkedList {
	Node<E> first; 第一个节点
    Node<E> last; 最后一个节点
   }

   //节点,双向链表
class Node<E> {
	E item; // 数据域
	Node<E> next; // 下一个节点
	Node<E> prev; // 上一个节点
}

2、LinkedList特有方法:
(1) void addFirst(E e) 在该列表开头插入指定的元素。
(2)void addLast(E e) 将指定的元素追加到此列表的末尾

(3)E getFirst() 返回此列表中的第一个元素
(4)E getLast() 返回此列表中的最后一个元素

(5) E removeFirst() 从此列表中删除并返回第一个元素
(6)E removeLast() 从此列表中删除并返回最后一个元素

public static void main(String[] args) {

   LinkedList<String> linked = new LinkedList<>();
   linked.add("长得帅");
   linked.add("张帅");
   linked.add("郝帅");
   linked.add("都狠帅");

   // void addFirst(E e) 在该列表开头插入指定的元素。
   linked.addFirst("赵德住");
   // void addLast(E e) 将指定的元素追加到此列表的末尾。
   linked.addLast("随便");
   System.out.println(linked); // [赵德住, 长得帅, 张帅, 郝帅, 都狠帅, 随便]

   // E getFirst() 返回此列表中的第一个元素。
   System.out.println(linked.getFirst()); // 赵德住
   // E getLast() 返回此列表中的最后一个元素。
   System.out.println(linked.getLast()); // 随便

   // E removeFirst() 从此列表中删除并返回第一个元素。
   linked.removeFirst();
   // E removeLast() 从此列表中删除并返回最后一个元素。
   linked.removeLast();
   System.out.println(linked);
}
4、List的其中一个子类:ArrayList集合

1、特点:

​ 因为底层结构是数组,所以查询块,增删慢

2、底层原理:

通过“看源码”,可以知道:(详细deBug见代码)

​ 1.ArrayList内部有一个成员变量Object[] elementData;,ArrayList存储数据就是存到了这个数组中.
​ 2.在构造方法中,初始化elementData为没有内容的数组
​ 3.在第一次调用add方法时,数组会初始化为长度10的数组
​ 4.调用add方法,当数组的容量不够的时候会进行扩容,新的容量是当前的1.5倍,10 -> 15 -> 22 -> 33(初始容量为0的时候扩容规则是0 ->10)。

public static void main(String[] args) {
    /*
        public class ArrayList {

          //本句的英文注释是:
         // The array buffer into which the elements of the ArrayList are stored.
        //(ArrayList内部有一个成员变量Object[] elementData;,ArrayList存储数据就是存到了这个数组中)

          transient Object[] elementData;//源码语句一

            //这句话是ArrayList的构造方法
            public ArrayList() {
                this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;;//源码语句二
                private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//源码语句三

                //(this.element指的是源码语句一中的数组。
                //等号右边表示是一个常量,源码中该常量等于{}.
            }

        }

        1.ArrayList内部有一个成员变量Object[] elementData;,ArrayList存储数据就是存到了这个数组中.
        2.在构造方法中,初始化elementData为没有内容的数组
        3.在第一次调用add方法时,数组会初始化为10个长度的数组
        4.调用add方法,当数组的容量不够的时候会进行扩容,新的容量是当前的1.5倍,10 -> 15 -> 22 -> 33
     */

    // 看源码:ctrl + 鼠标左键
    //点击等号左边的ArrayList,会到达该集合类的定义的地方;
    //点击等号左边的ArrayList,会到达该集合类的构造方法的地方

    //等号左边是数组变量;等号右边是创建对象
    ArrayList<Integer> list = new ArrayList<>();
    list.add(1);//打断点deBug看执行过程;forceInto键,首先进入的是Integer的valueOf方法中(先把基本数据类型1转化为Integer乐星
                //故要按 stepOut键退出该方法,再按stepInto键进入add方法
    list.add(2);
    list.add(3);
    list.add(4);
    list.add(5);
    list.add(6);
    list.add(7);
    list.add(8);
    list.add(9);
    list.add(10);
    list.add(11);
}
5、补充:Vector集合及Enumeration

1、特点:

​ java.util.Vector集合数和ArrayList一样底层使用数组结构。Vector底层使用的是数组,被同步,效率低,所以被淘汰了

2、Enumeration

​ Enumeration:英文翻译过来就是“枚举”,作用和迭代器是一样的(可以理解为迭代器的前身),可以获取集合的元素

​ vector这个类中有一个方法:elements(),调用该方法可以得到Enumeration,代码如下:

Vector<String> vec = new Vector<>();
Enumeration<String> enu = vec.elements();

3、Enumeration的2个方法?
(1)hasMoreElements: 判断是否有下一个元素
(2)nextElement(): 获取下一个元素

public static void main(String[] args) {
    Vector<String> vec = new Vector<>();
    vec.add("刘德华");
    vec.add("张学友");
    vec.add("黎明");
    vec.add("郭富城");

    // 迭代器
    Iterator<String> itr = vec.iterator();
    while (itr.hasNext()) {
        System.out.println(itr.next());
    }

    // Enumeration:英文翻译过来就是“枚举”,作用和迭代器是一样的,可以获取集合的元素
    //  hasMoreElements: 判断是否有下一个元素
    //  nextElement(): 获取下一个元素
    // elements()得到Enumeration
    Enumeration<String> enu = vec.elements();
    while (enu.hasMoreElements()) {
        System.out.println(enu.nextElement());
    }
}

4、Set接口及其子类集合

1、Set接口特点:

​ (1)没有索引,存取无序

​ (2)元素不可重复

2、Set接口的常用方法

boolean add(E e) 添加一个元素
boolean remove(Object o) 删除一个元素

​ PS:没有修改元素的方法(),也没有获取一个元素的方法(),只能通过迭代器来获取数据

3、Set接口的其中一个实现类:HashSet

​ 特点:(1)底层是哈希表

​ (2)a.没有索引,存取无序

​ b.元素不可重复

public static void main(String[] args) {
    HashSet<String> hs = new HashSet<>();
    hs.add("王昭君");
    hs.add("杨玉环");
    hs.add("貂蝉");
    hs.add("西施");
    hs.add("西施");
    hs.add("西施");
    hs.add("西施");
    // hs.remove("杨玉环");
    // 使用迭代器获取数据
    Iterator<String> itr = hs.iterator();
    while (itr.hasNext()) {
        System.out.println(itr.next());
    }
    //也可以使用增强for
        for (String el : hs) {
            System.out.println(el);
        }
}

/*
输出结果:(因为元素不可重复,所以没有多个西施)
貂蝉
王昭君
杨玉环
西施
*/
4、HashSet的其中一个子类:LinkedHashSet

1、特点:

​ 我们知道HashSet保证元素唯一,可是元素存放进去是没有顺序的,那么我们要保证有序,怎么办呢?
在HashSet下面有一个子类 java.util.LinkedHashSet ,它是链表和哈希表组合的一个数据存储结构。

​ (1)无索引

​ (2)存取有序

​ (3)元素不可重复

public class Demo11 {
    public static void main(String[] args) {
        LinkedHashSet<String> lhs = new LinkedHashSet<>();
        lhs.add("cc");
        lhs.add("bb");
        lhs.add("aa");
        lhs.add("dd");

        // 取出来
        Iterator<String> itr = lhs.iterator();
        while (itr.hasNext()) {
            System.out.println(itr.next());
        }

        // 增强for
        for (String el : lhs) {
            System.out.println(el);
        }
    }

5、哈希表

1、hashCode方法

​ (1)public native int hashCode(); 返回对象的哈希码值。

​ (2)被native修饰,是属于Object类的方法

​ (3)对象的hashCode相当于人的身份证的身份证作用,用于辨别。默认情况对象的hashCode就是对象的内存地址。

大多数时候,我们希望让hashCode的值反应的不是内存地址,而是反应对象的内容,所以我们会重写hashCode方法。,让hashCode值和对象的内容相关
(hashCode可能会重复,我们要尽量避免hashCode相同(系统有默认重写方案,已尽量避免)))

public static void main(String[] args) {
    // Person p1 = new Person("凤姐", 18);
    // System.out.println(p1); // 打印对象的地址: 50cbc42f
    // System.out.println(p1.hashCode()); // 打印对象的hashCode: 1355531311
    // System.out.println(Integer.toHexString(p1.hashCode())); // 打印对象的hashCode的十六进制: 50cbc42f

    //经过上述三行代码可知,hashCode方法默认返回的是对象的内存地址

    Person p2 = new Person("a", 18);
    Person p3 = new Person("b", 17);

    //重写了hashCode方法
    System.out.println(p2.hashCode());
    System.out.println(p3.hashCode());

    // String类已经重写了hashCode
    System.out.println("abc".hashCode());
    System.out.println("cba".hashCode());

    //即使重写了hashCode方法,也有可能出现hashCode值相同的情况
    System.out.println("通话".hashCode()); // 1179395
    System.out.println("重地".hashCode()); // 1179395
}
2、哈希表存储元素的过程(使用HashSet集合存储自定义元素)

1、哈希表的组成:

​ 由数组和链表组成。人们一般以水平是数组,垂直是链表的图像表示哈希表。如下图:

哈希表图示(此处模拟下哈希算法为: 位置 = 元素的hashCode % 10;)

在这里插入图片描述

2、说出哈希表存储元素的过程(哈希表判断元素唯一(元素不重复)的原理)
1.如果hashCode不相同,直接存储
2.如果hashCode相同,调用equals()方法
2.1 equals()返回false,存储
2.2 equals()返回true,不存储

结论:只有满足两个条件:(1)hascCode相同,(2)equals()方法返回true值,才认为是相同的元素,才会存如哈希表。

注意:

​ 只有底层使用哈希表结构的集合,才需要重写hashCode和equals以确保元素不重复(如集合HashSet/LinkedHashSet才需要重写)。

​ 但是如果元素是存储到ArrayList,LinkedList,则不需要重写.(因为这两个集合允许元素重复,没有必要使用hashCode。

3、哈希表的扩容介绍:

​ 我们知道,哈希表的数组存储的是索引值(即使用数组处理冲突),而链表才是真正存储元素数据的地方(同一hashCode值的元素都存储在同一链表当中)。所以当hashCode值相等的元素数据很多的时候,链表的长度可能会变得很长,这样的话通过key值依次查找的查询效率将会变得很低。故jdk1.8的时候,哈希表采用数组+链表+红黑树实现

​ 当链表长度(这里指的是和)超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。

​ 通过Api文档,查看到hashSet集合的构造方法,可以看到hashSet集合初始容量为16(数组的长度为16),负载因子为0.75.(简单理解负载因子:就是触发集合扩容时的集合已存储元素的比例)。即当我们哈希表中的元素数量超过容量的75%,也就是容量超过16*75%=12个元素(不是链表长度超过12个,而是总的哈希表存储的元素个数超过12个),就会触发扩容机制。

​ 这样的扩容机制会导致一个现象:部分链表会有“空”的现象,有些链表只有几个元素。这样的存储现象叫做哈希散列(通过哈希算法把元素数据散列在数组、链表上。

在这里插入图片描述

6、如何选择集合

到目前为止我们学习了常用的集合:
ArrayList,LinkedList,Vector,HashSet和LinkedHashSet

如何选择集合?

​ 1.如果元素不能重复
​ HashSet或者LinkedHashSet

​ 2.如果元素可以重复
​ 2.1 如果查询多使用: ArrayList
​ 2.2 如果增删多使用: LinkedList

​ 3.若没有目的意图,就默认使用ArrayList

7、红黑树

1、概念:

​ 趋近于平衡树,查询的速度非常快。**查找d的最大路径不能超过最小路径的两倍(以次尽量保证红黑树的平衡)。

2、特点:

​ (1)根节点是黑色的

​ (2)称空结点(实际上不存在的节点)为叶子节点,是长方形且黑色的。

​ (3)每个红色节点的子节点(不包括叶子节点)都是黑色的

​ (4)任何一个节点到其每一个叶子节点的所有路径上黑色节点数相同。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值