集合框架--List,Set,Map

集合的继承实现关系

请添加图片描述
请添加图片描述
Iterator迭代器,下面所有的都可以用迭代器来遍历。
Collecton接口常用的子接口有:List接口、Set接口。
List接口常用的子类有:ArrayList类、LinkedList类。
Set接口常用的子类有:HashSet类、LinkedHashSet类。
List允许有重复的元素,有序。
Set不予许有重复元素,无序。
保持顺序:以输入的顺序输出。

Collection接口概述

既然Collection接口是集合中的顶层接口,那么它中定义的所有功能子类都可以使用,是共性的功能,是集合中所有实现类必须拥有的方法
所有我们学习的时候首先从父类Collection开始,然后再学习子类中特有的内容。

Collection接口的基本方法
请添加图片描述

创建集合的格式

Collection<元素类型> 变量名 = new ArrayList<元素类型>();

只能存储<>中指定的元素类型,该方式为常用方式。

Collection 变量名 = new ArrayList();

集合的元素类型默认为Object类型,即任何类型的元素都可以存储。

示例

@Test
    public void collection(){
        Collection<String> collection = new ArrayList<>();
        collection.add("张三");
        collection.add("李四");
        collection.add("王五");
        collection.add("张三");
        System.out.println(collection);
        collection.remove("张三");
        System.out.println(collection);

        Object[] array = collection.toArray();
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i] + "  ");
        }
    }

[张三, 李四, 王五, 张三]
[李四, 王五, 张三]
李四 王五 张三

从Object继承来的toArray()方法可以将其转换为数组然后用数组的方式进行遍历。

Iterator迭代器

java中提供了很多个集合,它们在存储元素时,采用的存储方式不同。我们要取出这些集合中的元素,可通过一种通用的获取方式来完成。
Collection集合元素的通用获取方式:在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续在判断,如果还有就再取出出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。
集合中把这种取元素的方式描述在Iterator接口中。
请添加图片描述
Collection继承Iterable,Iterable里写了一个iterator()方法。
我们所写的Iterator接口里有两个常用的方法。
请添加图片描述
hasNext()方法:用来判断集合中是否有下一个元素可以迭代。如果返回true,说明可以迭代。
next()方法:用来返回迭代的下一个元素,并把指针向后移动一位。
在Collection接口描述了一个抽象方法iterator方法,所有Collection子类都实现了这个方法,并且有自己的迭代形式。

@Test
    public void iterator1() {
        Collection<String> collection = new ArrayList<>();
        collection.add("张三");
        collection.add("李四");
        collection.add("王五");
        collection.add("张三");
        Iterator<String> iterator = collection.iterator();//要先调用iterator()方法
        while (iterator.hasNext()) {//判断有没有下一个,有下一个返回true
            String data = iterator.next();//用来返回迭代的下一个元素,并把指针向后移动一位
            System.out.println(data);
        }
    }

张三
李四
王五
张三

可重复

List接口特点(也是所有实现类的特点)

1、它是一个元素存取有序的集合。例如,存元素的顺序是11、22、33。那么集合中,元素的存储就是按照11、22、33的顺序完成的)。
2、它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素(与数组的索引是一个道理)。
3、 集合中可以有重复的元素,通过元素的equals方法,来比较是否为重复的元素。

List接口的常用子类有:
ArrayList集合
LinkedList集合

ArrayList

1、增加元素方法
add(Object e):向集合末尾处,添加指定的元素
add(int index, Object e):向集合指定索引处,添加指定的元素,原有元素依次后移
2、删除元素删除
remove(Object e):将指定元素对象,从集合中删除,返回值为被删除的元素
remove(int index):将指定索引处的元素,从集合中删除,返回值为被删除的元素
3、替换元素方法
set(int index, Object e):将指定索引处的元素,替换成指定的元素,返回值为替换前的元素
4、查询元素方法
get(int index):获取指定索引处的元素,并返回该元素

@Test
    public void list1(){
        List<String> list = new ArrayList<String>();
        list.add("abc1");
        list.add("abc2");
        list.add("abc3");
        list.add("abc4");

        list.add(2, "Java");//List接口定义的方法
        Iterator<String> iterator = list.iterator();
        while (iterator.hasNext()) {
            String str = iterator.next();
            System.out.println(str);
        }
        System.out.println("--------------");

        //由于List集合拥有索引,因此List集合迭代方式除了使用迭代器之外,还可以使用索引进行迭代(类似数组)。
        for (int i = 0; i < list.size(); i++) {
            String str = list.get(i);
            System.out.println(str);
        }
        System.out.println("--------------");


        //迭代器的并发修改异常 java.util.ConcurrentModificationException
        //就是在遍历的过程中,使用了集合方法修改了集合的长度,不允许的
        //对集合使用迭代器进行获取,获取时候判断集合中是否存在 "abc3"对象
        //如果有,添加一个元素 "ABC3"
        Iterator<String> it = list.iterator();
        while(it.hasNext()){
            String s = it.next();
            //对获取出的元素s,进行判断,是不是有"abc3"
            if(s.equals("abc3")){
                list.add("ABC3");
            }
            System.out.println(s);
        }
    }

@Test
    public void list2() {
        List<String> list = new ArrayList<String>();
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("4");
        list.add("5");
        for (String item : list) {
            if (item.equals("3")) {
                System.out.println(item);
                list.remove(item);
            }
        }
        System.out.println(list.size());
    }

在这里插入图片描述
在这里插入图片描述

ConcurrentModificationException迭代器的并发修改异常

就是在遍历的过程中,使用了集合方法修改了集合的长度,不允许的,无论是添加还是删除,也不仅仅局限于迭代

那要是想添加的话怎么操作呢?
并发修改异常解决办法:在迭代时,不要使用集合的方法操作元素。
通过ListIterator迭代器操作元素是可以的,ListIterator的出现,解决了使用Iterator迭代过程中可能会发生的错误情况。

@Test
    public void list2() {
        List<String> list = new ArrayList<String>();
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("4");
        list.add("5");
//        for (String item : list) {
//            if (item.equals("3")) {
//                System.out.println(item);
//                list.remove(item);
//            }
//        }
        System.out.println(list.size());
        Iterator iterator = list.iterator();
        while (iterator.hasNext()) {
            String data = (String) iterator.next();
            if (data.equals("3")) {
                // 不管在foreach还是Iterator遍历过程中都不允许使用list改变长度
                // 如果是移除元素,可以使用Iterator里面的remove,但是对于添加
                // Iterator就没有提供,可以参考ListIterator
                iterator.remove();
                System.out.println(data);
            }
        }
        System.out.println(list.size());
    }

5
3
4

LinkedList:

LinkedList集合数据存储的结构是链表结构。方便元素添加、删除的集合。实际开发中对一个集合元素的添加与删除经常涉及到首尾操作,而LinkedList提供了大量首尾操作的方法
请添加图片描述
LinkedList是List的子类,List中的方法LinkedList都是可以使用,这里就不做详细介绍,我们只需要了解LinkedList的特有方法即可。在开发时,LinkedList集合也可以作为堆栈,队列的结构使用。

@Test
    public void list3() {
        LinkedList<String> link = new LinkedList<String>();
        //添加元素
        link.addFirst("abc1");
        link.addFirst("abc2");
        link.addFirst("abc3");
        //获取元素
        System.out.println(link.getFirst());
        System.out.println(link.getLast());
        //删除元素
        System.out.println(link.removeFirst());
        System.out.println(link.removeLast());

        while(!link.isEmpty()){ //判断集合是否为空
            System.out.println(link.pop()); //弹出集合中的栈顶元素
        }
    }

abc3
abc1
abc3
abc1
abc2

Vector:

Vector集合数据存储的结构是数组结构,为JDK中最早提供的集合。Vector中提供了一个独特的取出方式,就是枚举Enumeration,它其实就是早期的迭代器。此接口Enumeration的功能与 Iterator 接口的功能是类似的。Vector集合已被ArrayList替代。枚举Enumeration已被迭代器Iterator替代

Vector & ArrayList比较

相同点:Vector与ArrayList本质上都是一个Object[] 数组
不同点:
1)Vector是线程安全的集合类,ArrayList并不是线程安全的类。Vector类对集合的元素操作时都加了synchronized,保证线程安全。
2)Vector与ArrayList的扩容并不一样,Vector默认扩容是增长一倍的容量,Arraylist是增长50%的容量。ArrayList就有利于节约内存空间。
Vector无论查询还是增删都很慢,所以被ArrayList替代了。

Set接口

Set中元素无序不重复

HashSet

HashSet的底层是HashMap,HashMap底层由hashCode()与equals()方法实现
可以使用for和迭代器进行遍历。

无序:HashSet集合不能保证的迭代顺序与元素存储顺序相同。
不重复:HashSet集合,采用哈希表结构存储数据,保证元素唯一性的方式依赖于两个方法:hashCode()与equals()方法
主要的判断依据是地址,但是并不是简单的根据地址断定。当然想排除值相同的可以对这两个方法进行重写。

散列表(Hash table,也叫哈希表)

是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。

HashSet元素不能重复,如何实现?

当向哈希表中存放元素时,需要根据元素的特有数据结合相应的算法,这个算法其实就是Object类中的hashCode方法。由于任何对象都是Object类的子类,所以任何对象有拥有这个方法。即就是在给哈希表中存放对象时,会调用对象的hashCode方法,算出对象在表中的存放位置,这里需要注意,如果两个对象hashCode方法算出结果一样,这样现象称为哈希冲突这时会调用对象的equals方法,比较这两个对象是不是同一个对象,如果equals方法返回的是true,那么就不会把第二个对象存放在哈希表中,如果返回的是false,就会把这个值存放在哈希表中。
总结:保证HashSet集合元素的唯一,其实就是根据对象的hashCode和equals方法来决定的。如果我们往集合中存放自定义的对象,那么保证其唯一,就必须复写hashCode和equals方法建立属于当前对象的比较方式。
给HashSet中存储JavaAPI中提供的类型元素时,不需要重写元素的hashCode和equals方法,因为这两个方法,在JavaAPI的每个类中已经重写完毕,如String类、Integer类等。

例一

 @Test
    public void set1() {
        // 创建HashSet对象
        Set<String> set = new HashSet<String>();
        Set<Student> set1 = new HashSet<Student>();
        Student stu1 = new Student(1,"张三", 33, "20");
        Student  stu2 = new Student(1,"张三", 33, "20");
        // 给集合中添加自定义对象
        set.add("zhangsan");
        set.add("lisi");
        set.add("wangwu");
        set.add("zhangsan");

        set1.add(stu1);
        set1.add(stu2);

        for (String string : set) {
            System.out.println(string);
        }

        for (Student s1 : set1) {
            System.out.println(s1);
        }
    }

lisi
zhangsan
wangwu
Student{id=1, name=‘张三’, age=33, gender=‘20’}
Student{id=1, name=‘张三’, age=33, gender=‘20’}

stu1和stu2都能够加入HashSet里面,因为他们的地址不一样,但是这其实是同一个Student,
只是分别通过不同的方式new出来,如果stu1和stu2都能够加入的话,那么这个学生就在HashSet里面重复了。

例二

为什么对于第二组,堆中新new的,地址不一样的,在没有我们重写之前依然放不进去?
系统中String自己重写了hashCode()和equals()方法

@Test
    public void set2(){
        Set<String> set = new HashSet<String>();
        set.add("abc");
        set.add("abc");
        set.add("def");
        set.add(new String("def"));
        for (String s : set) {
            System.out.println(s);
        }
    }

abc

例:对Student类里的hashCode()与equals()方法

自定义类型,如果要使用HashSet,必须手动实现hashCode和equals。
我选择的判定方法是id和name,当这两项以当时就断定是相同的对象。
在这里插入图片描述
这个是自动加载的,不需要自己写。

import java.util.Objects;

public class Student {
    private int id;
    private String name;
    private int age;
    private String gender;
    。。。。。。
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return id == student.id && Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, name);
    }
}

进行方法的重写之后,输出:
Student{id=1, name=‘张三’, age=33, gender=‘20’}
已经只能添加上一个了。

Map接口

1、Collection中的集合,元素是孤立存在的(理解为单身),向集合中存储元素采用一个个元素的方式存储。
2、Map中的集合,元素是成对存在的(理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的值。
3、Collection中的集合称为单列集合,Map中的集合称为双列集合。
4、需要注意的是,Map中的集合不能包含重复的键,值可以重复;每个键只能对应一个值。
5、Map中常用的集合为HashMap集合、LinkedHashMap集合。
6、HashSet的底层是HashMap,HashMap底层由hashCode()与equals()方法实现
请添加图片描述
Map的子类:HashMap、LinkedHashMap

HashMap<K,V>

存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。

LinkedHashMap<K,V>

HashMap下有个子类LinkedHashMap,存储数据采用的哈希表结构+链表结构。通过链表结构可以保证元素的存取顺序一致;通过哈希表结构可以保证的键的唯一、不重复,需要重写键的hashCode()方法、equals()方法

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值