Java集合框架复习

1.集合的定义

计算机语言中有一个特别重要的容器就是数组,但是数组在Java中的长度是不可变的,如果想要存储任意个数的数据,用同一个数组是做不到的。对于不懂算法和数据结构的小白来说,就有很大的局限性。

1584691691917

这个问题Java在设计之初就考虑到了,所以Java的API中提供了一套完整集合体系,其实就是针对不同场景下提供的容器类以及方法,我们只要会使用这些类和方法就能很方便的完成增、删、改、查等功能。

Java的集合体系分为单列集合(Collection体系)和双列集合(Map体系)

在这里插入图片描述

单列集合

Collection接口是单列集合的根接口,该接口中定义了一些单列集合必须要有的方法。然后由实现类根据各自的数据结构来实现这些方法。

Collection接口
    -- List接口:有索引、有序、元素不唯一
        -- Vector
        -- ArrayList
        -- LinkedList
    -- Set接口:无索引、无序、元素唯一
        -- HashSet
            -- LinkedHashSet
        -- TreeSet

双列集合

Map接口是双列集合的根接口,该接口中定义了一些双列集合必须要有的方法。然后由实现类根据各自的数据结构来实现这些方法。

Map接口
    -- Hashtable
        -- Properties
    -- HashMap
        -- LinkedHashMap
    -- TreeMap

2.Collection集合

Collection是单列集合的根接口,该接口中定义了所有单列集合中必须要有的方法。任何一个Collection接口的实现类都有对这些方法进行实现。

在这里插入图片描述

下面我们就来学习一下这些方法的作用以及如何使用。

public boolean add(E e)
   添加元素到集合末尾
public boolean addAll(Collection c)
   往集合中添加另一个集合中的元素。
public boolean remove(Object o)
   删除集合中指定的元素,如果删除成功返回true.
public boolean contains(Object o)
   判断集合中是否包含指定元素。如果包含返回true,不包含返回false.
public boolean isEmpty()
   判断集合是否为空(当集合长度为0时,即为空)。如果为空返回true,不为空返回false.
public void clear()
   清空集合中的元素
public int size()
   获取集合的长度(元素个数)
public Iterator iteartor()
   获取集合的迭代器
public Object[] toArray()  
   把集合转换为数组

eg:
增删改查
//<String>:表示集合中的元素是String类型
Collection<String> coll=new ArrayList<>();
//增加元素
coll.add("孙悟空");
coll.add("猪八戒");
coll.add("沙和尚");
coll.add("唐三藏");
System.out.println(coll); // [孙悟空,猪八戒,沙和尚,唐三藏]

//删除元素
coll.remove("孙悟空");
System.out.println(coll); // [猪八戒,沙和尚,唐三藏]

//判断是否包含"孙悟空"
boolean b1=coll.contains("孙悟空");
System.out.println(true); //false,因为前面已经删除“孙悟空”了

//判断集合是否为空
boolean b2=coll.isEmpty();
System.out.println(b2);//false,集合中还有元素

//清空集合
coll.clear(); 

//获取集合的长度
int length=coll.size();
System.out.println(length); //0 

//再次判断集合是否为空
boolean b3=coll.isEmpty();
System.out.println(b2);//true,集合中已经没有元素了.

//演示把集合转换为数组,数组中的元素类型会自动提升为Object类型。
Object[] array=coll.toArray();

迭代器遍历
迭代其实就说获取的意思,迭代器就是用来从集合中获取元素的一种方式。

//<String>:表示集合中的元素是String类型
Collection<String> coll=new ArrayList<>();
//增加元素
coll.add("孙悟空");
coll.add("猪八戒");
coll.add("沙和尚");
coll.add("唐三藏");

//获取集合的迭代器
Iterator<String> it=coll.iterator();
//判断是否有元素可以迭代
while(it.hashNext()){
    //获取下一个元素
    String str=it.next();
    System.out.println(str); //打印元素
}

3.List集合

了解了Collection集合体系中共性的方法之后,接下来我们继续了解它的子接口List中特有的方法以及List接口的常用实现类。

1584761062100

Java的API中明确说明List集合(指的是List接口以及它的所有实现类)是有索引的、元素可以重复的、有序的集合。也就是说List集合可以根据索引对元素进行精确的控制,例如在指定索引位置添加元素、删除元素、修改元素、查询元素等等。

List接口特有方法
public void add(int index,E e)
    在集合的指定索引位置添加元素
public E remove(int index)
    从集合中删除指定索引的元素
public E set(int index,E e)
    修改集合中指定索引的元素为新的元素。
public E get(int index)
    获取集合中指定索引的元素
3.1.ArrayList集合

ArrayList类的底层数据结构是数组,按照数组的原理对List接口中的方法进行了实现。

数组结构的特点是:查询快、增删慢
【数组简介】
数组的每一个元素都有索引,只要通过索引就可以对元素进行增、删、改、查的操作。
1584774973677

当创建一个ArrayList对象时,底层其实会初始化一个容量为16的数组,随着向 ArrayList 中不断添加元素,其容量也自动增长。

集合中存储整数

public class ArrayListDemo1{
    public static void main(String[] args){
        //创建ArrayList集合对象
        List<Integer> list=new ArrayList<>();
        //顺序添加元素
        list.add(9);
        list.add(8);
        list.add(7);
        list.add(6);
        System.out.println(list); //[9,8,7,6]

        //在1索引位置添加元素
        list.add(1,3); //在1索引位置添加元素3
        System.out.println(list); //[9,3,8,7,6]

        //删除2索引位置的元素
        list.remove(2);
        System.out.println(list); //[9,3,7,6]

        //获取3索引的元素
        Integer num=list.get(3);
        System.out.println(num); //6

        //遍历集合中的元素
        for(int i=0;i<list.size();i++){
            //获取i索引位置的元素
            Integer e=list.get(i);
            System.out.println(e); 
        }
    }
}

集合中存储自定义对象

集合中的存储的元素也可以是自定义的类型,例如往集合中添加若干个Student对象

public class Student{
    private String name;
    private int age;

    public Student(String name,int age){
        this.name=name;
        this.age=age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public class ArrayListDemo2{
    public static void main(String[] args){
         //<Student>表示集合中的元素是Student对象
        List<Student> list=new ArrayList<>();
        list.add(new Student("孙悟空",20));
        list.add(new Student("猪八戒",18));
        list.add(new Student("沙和尚",21));
        list.add(new Student("唐三藏",23));

        //遍历集合中的元素,每一个元素是一个Student对象
        for(int i=0;i<list.size();i++){
            Student stu = list.get(i);
            System.out.println(stu); //底层会自动调用toString()方法
        }
    }
}
3.2.LinkedList集合

LinkedList集合底层是链表结构,按照链表结构的特点对List接口中的方法进行了实现。

链表结构的特点是:查询慢、增删快
【链表简介】
链表是由一个一个的节点组成,一个节点通过地址指向下一个节点,类似于一条链子。
LinkedList集合中的每一个元素其实就是链表中的一个节点。
1584777613317

LinkedList类还为集合在开头和结尾提供了便捷的操作方法,而且这些方法有统一的命名规则,方便开发者调用。

public void addFirst(E e)  
    在集合开头添加元素
public void addLast(E e)
    在集合结尾添加元素
public E removeFirst() 
    移除第一个一个元素
public E removeLast()
    移除最后一个元素
public E getFirst()
    获取第一个元素
public E getLast()
    获取最后一个元素

利用以上方法,可以使用LinkedList集合来模拟栈结构

栈结构的特点是:先进后出
【栈结构简介】
    1)栈结构就像一个子弹夹,子弹夹中的子弹就是元素
    2)先放进去的元素在栈底,后放进去的元素在栈顶。
    3)每次添加、获取元素时,只能操作栈顶的元素。

在这里插入图片描述

LinkedList类中还提供了两个用于进栈和出栈的方法。

public void push(E e)
    将元素推入此集合的栈顶。换句话说,就是将元素添加到集合开头的位置
public E pop()
    从集合的栈顶处弹出一个元素。换句话说,移除并返回此列表的第一个元素。

4.Set集合

接下来我们来学习Collection体系中的另一个子接口Set以及它的实现类。查看API文档发现,Set相对于Collection而言没有什么特有方法,也就是说Set集合中的方法都是复写的Collection的方法。

1584779997735

Set集合和前面其他集合的使用方式都是一样的,无非就是增、删、改、查以及遍历等功能,但是我们要知道每一种集合的底层对增、删、改、查等功能的实现方式是不一样的。

4.1.HashSet集合

HashSet集合底层是哈希表结构,它不保证元素的迭代顺序,不能包含重复的元素。

1584805612677

哈希表特点:不保证元素的迭代顺序,也不能包含重复的元素。
【哈希表简介】
    哈希表是由数组+链表组成的一种数据结构,当往HashSet集合中添加元素时,原理如下
    1.先计算元素的hashCode值,再把哈希值转换为数组的索引i
    2.如果i索引位置为null,就在i索引位置该创建一个节点
    3.如果i索引位置不为null,就判断i索引位置的元素和即将添加的元素hash值是否一样。
    4.如果hash值相同,继续让i索引的元素和即将添加的元素用equals进行比较
    5.如果hash值和equals比较都相同,就认为元素重复。
    6.如果hash值相同,equals比较不同,以链表的形式链接到一起。

【HashSet如何保证元素唯一性】
    1.复写元素的hashCOde方法
    2.复写元素的equals方法

了解了HashSet集合的特点之后,我们往HashSet集合中添加Student类型,为了保证元素的唯一性,就必须复写hashCode和equals方法.

public class Student{
    private String name;
    private int age;

    //...此处省略构造方法、get和set方法...

    //只要name和age一样,那么equals方法就返回true
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }

    //只要name和age一样,那么hashCode值就一样。
    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}
public class HashSetDemo1{
    public static void main(String[] args){
        Set<Student> set=new HashSet<>();
        //添加元素到集合
        set.add(new Student("孙悟空",20));
        set.add(new Student("猪八戒",18));
        set.add(new Student("沙和尚",29));
        set.add(new Student("孙悟空",20));

        //迭代器遍历集合
        for(Student s:set){
            System.out.println(s.getName()+","+s.getAge());
        }
    }
}
4.2.LinkedHashSet

LinkedHashSet集合是HashSet的子类,它和HashSet的区别就是LinkedHashSet能保证元素的迭代顺序,其他特点和用法都是一样的。

public class HashSetDemo2{
    public static void main(String[] args){
         Set<Student> set1 = new HashSet<>();
        //添加元素到集合
        set1.add(new Student("猪八戒", 18));
        set1.add(new Student("孙悟空", 20));
        set1.add(new Student("沙和尚", 29));
        set1.add(new Student("孙悟空", 20));

        //迭代器遍历集合
        for (Student s : set1) {
            System.out.println(s.getName() + "," + s.getAge());
        }

        System.out.println("----------------------");

        Set<Student> set2 = new LinkedHashSet<>();
        //添加元素到集合
        set2.add(new Student("孙悟空", 20));
        set2.add(new Student("猪八戒", 18));
        set2.add(new Student("沙和尚", 29));
        set2.add(new Student("孙悟空", 20));
        for (Student s : set2) {
            System.out.println(s.getName() + "," + s.getAge());
        }
    }
}

在这里插入图片描述

4.3.TreeSet集合

TreeSet的底层是红黑树数据结构,底层比较复杂,可以参考其他博文来了解红黑树的知识,后面我会单独写一篇博文来记录红黑树,下图是红黑数的结构图,它也叫二叉树排序树,观察下图不难发现有如下特点。
红黑树可以参考这篇文章:http://t.csdn.cn/7iDy7

1.任意一个节点都是一个元素,每一个元素最多有2个分支
2.每一个元素的左边,都是比这个元素小的数据
3.每一个元素的右边,都是比这个元素大的数据

当遍历元素时从最左边的元素开始,按照中序遍历获取元素,就可以获取到大小有序的元素.

在这里插入图片描述

自然排序

通过查看API和源码会发现IntegerString都实现了一个排序的接口Comparable,该接口中有一个compareTo方法,用于对元素进行比较。每次往TreeSet集合中添加元素时会自动调用compareTo方法,根据该方法的返回值是负数、正数、或者0来决定把这个元素放在树的左边,右边还是不存储。

下面在集合中存储String和Integer类型的数据,使用TreeSet集合对元素进行自然排序。

public class TreeSetDemo1{
    public static void main(String[] args){
        //创建TreeSet集合对象,存储String类型元素
        Set<String> set1=new TreeSet<>();
        set1.add("aaa");
        set1.add("ccc");
        set1.add("bbb");
        //遍历集合
        for(String s:set1){
            System.out.println(s); //打印元素分别是 aaa,bbb,ccc
        }

        System.out.println("--------------");

        //创建TreeSet集合对象,存储Integer类型元素
        Set<Integer> set1=new TreeSet<>();
        set1.add(8);
        set1.add(6);
        set1.add(7);
        //遍历集合
        for(Integer s:set2){
            System.out.println(s); //打印元素分别是 6,7,8
        }
    }
}

在TreeSet集合中存储Integer对象和String对象可以自动排序,那是因为Integer和String已经实现了Comparable接口。

同理我们可以让自定义的Student类型实现Comparable接口,复写compareTo方法,同样也能让Student对象按照指定规则进行排序。

public class Student implements Comparable<Student>{
    private String name;
    private int age;
    //构造方法,get和set方法

    @Override
    public int compareTo(Student o){
        //按照年龄的升序排列
        int num=this.age-o.age;
        //如果年龄相同,按照姓名排序
        if(num==0){
            num=this.name.compareTo(o.name);
        }
        return num;
    }
}
定义测试类,在TreeSet集合中存储Student对象

public class TreeSetDemo2{
    public static viod main(String[] args){
        Set<Student> set=new TreeSet<>();
        //往TreeSet集合中添加Student对象
        set.add(new Student("a孙悟空",500));
        set.add(new Student("b小白龙",500));
        set.add(new Student("c猪八戒",300));
        set.add(new Student("d沙和尚",400));
        //遍历集合
        for(Student s:set){
            System.out.println(s.getName()+","+s.getAge());
        }
    }
}

运行结果如下

在这里插入图片描述

如果Student类没有实现Comparable接口,就会出现下面的异常。

1584860234649

自定义比较器器排序

Java的API中还提供了一个Compartor接口,用于对两个元素进行比较,而具体比较规则需要我们自己实现。TreeSet提供了一个构造方法,在创建TreeSet对象时传递一个自定义的Compartor实现类对象,也可以让元素进行排序。这种排序方法叫做自定义比较器排序。

使用自定义Comparator实现类,对TreeSet元素进行排序。

public class TreeSetDemo3{
    public static void main(String[] args){
        //创建TreeSet集合,指定自定义比较器
        Set<Student> set=new TreeSet<>(new Comparator<Student>(){
            @Override
            public int compare(Student o1,Student o2){
                //按照年龄的升序排列
                int num=this.age-o.age;
                //如果年龄相同,按照姓名排序
                if(num==0){
                    num=this.name.compareTo(o.name);
                }
                return num;
            }
        });

        //添加Student对象元素
        set.add(new Student("a孙悟空",500));
        set.add(new Student("b小白龙",500));
        set.add(new Student("c猪八戒",300));
        set.add(new Student("d沙和尚",400));
    }
}

运行结果如下

1584860329881

5.Map体系结构

Map集合(Map以及Map的实现类)是双列集合,集合中的元素是以【键-值对】的形式存在的。下图是Map集合的体系结构。

在这里插入图片描述

上面每一种集合的用法其实是一样的,因为它们的方法都实现自Map接口。接下来看一下Map接口中的方法。

Map常用方法
public V put(K key, V value)  
    往Map集合中添加键值对.返回被覆盖键的值,如果键相同.把值修改了
public V remove(Object key)
    根据键删除键值对.返回被删除的值
public void clear()   
    清空集合中的元素
public int size()
    获取集合中【键值对】的个数
public V get(Object key)  
    根据键获取值
public Set<K> keySet()  
    获取所有键的集合
public Collection<V> values()
    获取所有值的集合
public Set<Map.Entry<K,V>> entrySet()
    获取所有的【键值对】
public boolean containsKey(Object key)
    判断键是否存在
public boolean containsValue(Object value)  
    判断值是否存在
public boolean isEmpty()
    判断集合中是否有元素
5.1.HashMap

HashMap底层数据结构是哈希表,前面讲到HashSet底层结构也是哈希表。其实这样两个集合本质上是一样的。为什么这么说呢?查看源码会发现,当使用HashSet的add方法添加元素时,内部是在调用HashMap的put方法,元素当做HashMap的键来存储。所以HashSet元素的特性,和HashMap键的特性是完全一样的。

HashSet如何保证元素唯一性
    先判断元素的hashCode值和集合中元素是否一致
    再使用equals判断元素和集合中已有元素是否相同
    如果hashCode和euqals都相同,则认为元素重复。
    【为了保证元素唯一,需要复写元素hashCode和equals方法】

HashMap如何保证键的唯一性
    先判断键的hashCode值和集合中元素是否一致
    再使用equals判断键和集合中已有的键是否相同
    如果键的hashCode和euqals与集合中的键相同,则认为元素重复。
    【为了保证元素唯一,需要复写键的hashCode和equals方法】
keySet()遍历

调用keySet()方法先得到Map集合中所有的键,然后再通过get方法根据键获取值。

public class MapDemo1{
    public static void main(String[] args){
        Map<String,Integer> map =new HashMap<String,Integer>();
        map.put("李四", 6000);
        map.put("张三", 7889);
        map.put("张三", 7000);
        map.put("王五", 9000);
        //获取所有的键
        Set<String> keys=map.keySet();
        //遍历键的集合
        for(String key:keys){
            //通过键.获取值
            String value=map.get(key);
            System.out.println(key+"="+value);
        }
    }
}

enterySet()遍历

entrySet()方法可以获取到所有的【键值对】组成的Set集合,键值对用Map.Entry表示,其中K表示键的类型,V表示值得类型。

public class MapDemo2{
    public static void main(String[] args){
        //在Map集合中存储自定义对象Student作为键,String作为值
        HashMap<Student,String> map=new HashMap<Student,String>();
        //添加键和值
        map.put(new Student("张三",18), "武汉");
        map.put(new Student("王五",20), "北京");
        map.put(new Student("李四",19), "南京");
        map.put(new Student("张三",18), "上海");
        //使用entrySet方法遍历map集合.Student作为键
        Set<Entry<Student, String>> entrys = map.entrySet();
        for (Entry<Student, String> entry : entrys) {
            Student key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key+"...."+value);
        }
    }
}
5.2.LinkedHashMap

LinkedHashMap是HashMap的子类,用法和HashMap类似。区别就是HashMap中的键是无序的,而LinkedHashMap的键是有序的。

public class MapDemo3{
    public static void main(String[] args){
        Map<String, Integer> map1 = new HashMap<>();
        map1.put("孙悟空", 500);
        map1.put("沙和尚", 300);
        map1.put("猪八戒", 200);
        map1.put("孙悟空", 700);
        //获取所有的键
        Set<String> keys1 = map1.keySet();
        //遍历键的集合
        for (String key : keys1) {
            //通过键.获取值
            Integer value = map1.get(key);
            System.out.println(key + "=" + value);
        }

        System.out.println("----------------------");

        Map<String, Integer> map2 = new LinkedHashMap<>();
        map2.put("孙悟空", 500);
        map2.put("沙和尚", 300);
        map2.put("猪八戒", 200);
        map2.put("孙悟空", 700);
        //获取所有的键
        Set<String> keys2 = map2.keySet();
        //遍历键的集合
        for (String key : keys2) {
            //通过键.获取值
            Integer value = map2.get(key);
            System.out.println(key + "=" + value);
        }
    }
}

1584863944690

5.3.TreeMap

TreeMap底层是红黑数结构,它可以对键进行排序。前面的TreeSet集合底层就是TreeMap

所以TreeSet元素的特点和TreeMap键的特点是完全一样的。

//定义Student类实现 Comparable<Student>接口,让学生具备可以排序的规则。
public class Student implements Comparable<Student>{
    private String name;
    private int age;
    //构造方法,get和set方法

    @Override
    public int compareTo(Student o){
        //按照年龄的升序排列
        int num=this.age-o.age;
        //如果年龄相同,按照姓名排序
        if(num==0){
            num=this.name.compareTo(o.name);
        }
        return num;
    }
}

//定义测试类
public class MapDemo4{
    public static void main(String[] args){
        Map<Student,String> map=new TreeMap<>();
        //添加键和值
        map.put(new Student("张三",18), "武汉");
        map.put(new Student("王五",20), "北京");
        map.put(new Student("李四",19), "南京");
        map.put(new Student("张三",18), "上海");
        //使用entrySet方法遍历map集合.Student作为键
        Set<Entry<Student, String>> entrys = map.entrySet();
        for (Entry<Student, String> entry : entrys) {
            Student key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key+"...."+value);
        }
    }
}

运行结果如下

1584864858548

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值