java云同桌学习系列(七)——集合

    科技2022-08-08  110

    本博客java云同桌学习系列,旨在记录本人学习java的过程,并与大家分享,对于想学习java的同学,我希望这个系列能够鼓励大家一同与我学习java,成为“云同桌”。

    每月预计保持更新数量三章起,每章都会从整体框架入手,介绍章节所涉及的重要知识点及相关练习题,并会设置推荐学习时间,每篇博客涉及到的点都会在开篇目录进行总览。(博客中所有高亮部分表示是面试题进阶考点)

    集合

    1.常见数据结构简介(1)栈(stack)(2)队列(queue)(3)链表(linked list)(4)二叉树(binary tree) 2.Collection接口及其分支java.util.Collection(interface)java.util.List(interface)java.util.ArrayListjava.util.Vectorjava.util.LinkedListjava.util.Iterator(Interface) 3.forEach循环语句4.Set接口相关类java.util.Set(interface)java.util.HashSetjava.util.TreeSet 5.哈希表概述6.Map接口相关类java.util.Map(interface)java.util.HashMap

    学习时间:一周 学习建议:集合相当于数组的进阶版本,未来可能会频繁使用集合开发,建议弄清楚各集合类之间的区别和其特点,不同问题采用不同集合解决

    1.常见数据结构简介

    (1)栈(stack)

    栈又称堆栈,栈限定只能在线性表表尾进行插入和删除的操作,这一端允许插入和删除称为栈顶,另一端称为栈底,不含任何数据元素的称为空栈。

    特点:

    (1)先进先出 (2)栈的入口和出口在一端,都位于栈顶

    图解:

    专业术语: 压栈:存元素 弹栈:取元素

    (2)队列(queue)

    队列,简称队,只允许在队尾(rear)进行插入,只允许在队头(front)进行元素删除的操作。空队列是不含任何元素的空表

    特点:

    (1)先进先出 (2)队列的出口在一侧,入口在另一侧

    图解:

    (3)链表(linked list)

    以单向链表为例,链表是有一系列(包含存储数据元素的数据域与下一个结点地址的指针域)结点组成,结点数量可以动态生成

    图解:

    特点:

    (1)内存地址不连续 (2)长度可以动态增加 (3)查找元素较慢,增删元素较快

    结点结构:

    private class Node{ private T t; private Node next; }

    (4)二叉树(binary tree)

    通常java中常用的二叉树指,每个结点不超过2的有序树。即如何一个数比根结点数据更下,则存放在根结点的左子树,如果一个数比根结点数据更大,则存放在根结点的右子树,结点与结点之间依靠父与左右子的关系建立。

    有序二叉树结点结构

    public class Node { private int data; private BNode leftNode; private BNode rightNode; }

    2.Collection接口及其分支

    Java中为了方便用户操作各个数据结构,引入了类集的概念,所有的类集的操作的接口或类都在java.util包中。

    java.util.Collection(interface)

    介绍:Collection 接口是在整个 Java 类集中保存单值的最大操作父接口,里面每次操作的时候都只能保存一个对象的数据。在开发中,通常不会直接使用Collection接口,而使用其操作的子接口,List(允许重复),Set(不允许重复)

    常用方法:

    add(E e), 向集合中添加元素size(),返回此集合中的元素数量iterator(),返回集合中的元素迭代器toArray(),返回包含此集合中所有元素的一个数组。

    java.util.List(interface)

    介绍:java.util.List 接口继承自 Collection

    接口特点:

    List集合中允许出现重复元素 可以通过索引访问集合中任一个元素 所有元素以线性方式存储,元素的存入顺序和取出顺序一致

    接口常用的实现类: ArrayList(95%)、Vector(4%)、LinkedList(1%)

    常用方法(不止继承了Collection接口的全部方法,还实现或建立了一些方法):

    public void add(int index, E element) : 将指定的元素,添加到该集合中的指定位置上。public E get(int index) :返回集合中指定位置的元素。public E remove(int index): 移除列表中指定位置的元素, 返回的是被移除的元素。public E set(int index, E element) :用指定元素替换集合中指定位置的元素,返回值的更新前的元素。public ListIterator listIterator() :返回 ListIterator 接口的实例

    java.util.ArrayList

    介绍:ArrayList是List接口的实现类,使用最为广泛

    特点:

    线程不安全 数组结构,增加删除慢,查找快,可动态调整大小(初始默认为10,每次扩容为原来的1.5倍)

    构造方法:

    ArrayList(),构造一个初始容量为10的空列表。ArrayList(int initialCapacity),构造具有initialCapacity大小初始容量的空列表。

    常用方法(无特别方法,常用方法均继承于List接口)

    使用举例:

    ArrayList<Integer> arrayList = new ArrayList<>(); arrayList.add(100);arrayList.add(200); System.out.println(arrayList.get(1));//200 System.out.println(arrayList);//[100, 200]

    java.util.Vector

    介绍:大多数与ArrayList类使用的相同,它是为了解决线程不安全的问题,Vector是同步的, 如果不需要线程安全实现,建议使用ArrayList代替Vector

    特点:

    线程安全 数组结构,增加删除慢,查找快,可动态调整大小(初始默认为10,每次扩容倍数可指定)

    构造方法:

    Vector(),构造一个空向量,使其内部数据数组的大小为 10 ,其标准容量增量为零。Vector(int initialCapacity),构造一个具有指定初始容量且容量增量等于零的空向量。Vector(int initialCapacity, int capacityIncrement),构造具有指定初始容量和容量增量的空向量。

    常用方法(无特别方法,常用方法均继承于List接口)

    java.util.LinkedList

    介绍:List接口的实现类之一,双链表结构,可以使用其中的一些方法实现队列和栈数据结构

    特点:

    双链表结构,增删快,查找慢 线程不安全

    常用方法:(继承了一些List接口的方法,还有一部分新的方法)

    add(E e),将指定的元素追加到此列表的末尾。addFirst(E e),在此列表的开头插入指定的元素。addLast(E e),将指定的元素追加到此列表的末尾。getFirst(),返回此列表中的第一个元素。getLast(),返回此列表中的最后一个元素。removeFirst(),从此列表中删除并返回第一个元素。removeLast(),从此列表中删除并返回最后一个元素。

    可通过上述的方法,实现栈数据结构(笨比版)

    //栈数据结构,先进后出 LinkedList<Integer> linkedList = new LinkedList<>(); linkedList.addFirst(1); linkedList.addFirst(2); System.out.println(linkedList.removeFirst());//2 System.out.println(linkedList.removeFirst());//1

    实现队列数据结构

    //队列数据结构,先进先出 LinkedList<Integer> linkedList = new LinkedList<>(); linkedList.addFirst(1); linkedList.addFirst(2); System.out.println(linkedList.removeLast());//1 System.out.println(linkedList.removeLast());//2 push(E e),将元素推送到此列表所表示的堆栈上。pop(),弹出此列表所代表的堆栈中的元素。

    类中提供了专门模拟堆栈的方法(高端版)

    //栈数据结构,先进后出 LinkedList<Integer> linkedList = new LinkedList<>(); linkedList.push(1); linkedList.push(2); System.out.println(linkedList.pop());//2 System.out.println(linkedList.pop());//1

    java.util.Iterator(Interface)

    介绍:通常称为迭代器,可以对List集合和set集合进行遍历,从中取出数据,遍历过程可以理解为游标的概念,初始迭代器指针指向0下标的上一位,迭代器指针下移后,指到哪一位代表操作哪一位

    特点:

    通过集合对象继承下来的iterator()方法获取iterator对象 Iterator iterator = arrayList.iterator();

    常用方法:

    hasNext(),判断迭代器指针下一位有没有数据元素,根据结果返回boolean类型数next(),迭代器指针向下移一位,并返回所指数据(注意指针初始在0下标上方,移动一次后才会指向0下标)

    常搭配使用,指针向下移动遍历输出集合元素

    ArrayList arrayList = new ArrayList(); arrayList.add(1); arrayList.add(2); arrayList.add(3); Iterator iterator = arrayList.iterator(); while(iterator.hasNext()){ System.out.println(iterator.next()); } /*输出: 1 2 3 */ add(E e),在迭代器指针所指位置,插入数据remove(),在迭代器指针所指位置,删除元素,并返回所删除的元素。set(E e),在迭代器指针所指位置,修改数据

    3.forEach循环语句

    forEach又称为增强for循环,用于迭代数组或集合(Collection下的集合)

    语法:

    for(数据类型 存储数据元素的变量:集合或者数组名){ 循环体 }

    对于集合,原理相当于为集合创建了一个迭代器,然后遍历,后续知识点中将会有实例

    4.Set接口相关类

    java.util.Set(interface)

    介绍:Set接口也是Collection接口下的一个主要接口,是存储无重复元素的集合

    常用方法:

    iterator(),返回一个set中元素的迭代器

    相比于List接口,Set接口并没有自己写一个用于取出数据的get()方法,因此,对于取出数据,只能通过获取迭代器,然后迭代器移动到指定位置后取出数据

    此接口中有两个常用的子类:HashSet、TreeSet

    java.util.HashSet

    介绍:该类是Set接口的常用实现类之一,存储元素时会检测是否是重复元素,根据返回值决定是否存入数据,采用散列存储结构(哈希表)

    特点:

    1.散列存储结构 2.数据元素无序 3.无重复数据元素(重复则存不进去)

    因为没有Set接口中没有get()方法,取数据时,可以获取迭代器遍历,或者直接使用forEach循环结构进行取出数据

    java.util.TreeSet

    介绍:该类是Set接口的常用实现类之一,采用有序二叉树的数据结构

    特点:

    * 有序二叉树的存储结构 * 数据元素有序(自然顺序,对于自己编写的类对象等进行排序会出错,需要重写) * 迭代器是快速失败的(指在原集合上迭代,集合元素改变时迭代器会报异常,没有特别说别的话,其他集合都是安全失败迭代器,即另外复制一份用于迭代) * 无重复数据元素(重复则存不进去) * 线程不同步,不安全

    对于自然有序的数据,常使用以下代码进行输出

    TreeSet<Character> treeSet = new TreeSet<Character>(); treeSet.add('c'); treeSet.add('b'); treeSet.add('a'); //forEach循环结构 for(char i:treeSet){ System.out.println(i); } /* 输出: a b b */

    对于非自然有序的数据,即要存入自己定义的类对象,系统不知道到那个对象是较大的,会报错,此时就需要我们来制定比较规则;

    通过将待比较的类实现Compparable接口,重载实现CompareTo抽象方法描述比较规则,返回具备大小的一系列int返回值

    TreeSet<Person> treeSet = new TreeSet<Person>(); Person p1 = new Person(1); Person p2 = new Person(2); Person p3 = new Person(3); treeSet.add(p1); treeSet.add(p2); treeSet.add(p3); for(Person p:treeSet){ System.out.println(p); } //Person类 public static class Person implements Comparable<Person>{ private int age; //重载比较规则方法 @Override public int compareTo(Person o) { //将this对象属性与o对象属性进行比较 if(this.age > o.age){ return 2; }else if(this.age == o.age){ return 1; }else{ return 0; } } @Override public String toString() { return "Person{" + "age=" + age + '}'; } public Person(int age) { this.age = age; } }

    5.哈希表概述

    结构:可以简单理解成“数组+链表”的结构,它根据关键码值(key value)映射到数组中的一个位置,对于位置冲突的情况,在此建立链表,尾插入新数据

    对于上图结构,某个数组位置及其链表,专业术语称之为“哈希桶”,当哈希桶中数据过多,就会降低表的性能,不便于查找

    因此,如果哈希桶>8,哈希桶内的链表就会转化成红黑树(一种平衡二叉查找树),然后当哈希桶减少到6时,红黑树又转变为链表

    初始属性:初始哈希表长度:16,散列因子:0.75

    散列因子:以0.75为例,当哈希表长度的75%已经被占用,则进行扩容为原长度的2倍,散列因子官方建议为0.75效率最高,散列因子过小,会造成空间浪费,过大,会导致检索速度变慢

    如果数据较多,只设置长度为初始长度,需要多次扩容,造成内存的大量占用,建议数据较多时指定一个合适的初始长度

    注意:非必要情况,不要修改已经存入哈希表内的键,否则容易造成数据错乱

    6.Map接口相关类

    java.util.Map(interface)

    介绍:Map其实是mapping(映射)的缩写,相比于Collection接口存储单值,Map以一个个键值对的形式存储多个值,每个键名互不重复

    特点:

    * 键值对形式的多值存储 * 键值对的键名互不重复 * 存储不连续,无法像集合一样直接遍历

    常用方法:

    clear(),清空此映射中的所有键值对映射get(Object key),通过键名获取返回键名指定的数据remove(Object key),通过键名删除所对应的数据,并返回该被删除的数据keySet(),解决遍历问题,以set集合的形式返回此映射的所有键名put(K key, V value),将一个键值对关系存入此映射中,如果此键名在此映射中已有对应的数据,则返回旧数据,否则返回nullcontainsKey(Object key),如果此映射包含指定键的映射,则返回 true 。containsValue(Object value),如果此映射将一个或多个键映射到指定值,则返回 truesize(),获取此映射中键值对的数量

    java.util.HashMap

    介绍:基于哈希表的Map接口的实现,可以构造哈希表

    特点:

    线程不同步 ,允许键值为null

    构造方法:

    HashMap(),构造一个空HashMap,使用默认初始容量(16)和默认加载因子(0.75)HashMap(int initialCapacity),构造一个空HashMap,使用初始容量(initialCapacity)和默认加载因子(0.75),建议对于大量数据存储,给定一个合适的初始容量,可以降低扩容次数,减少内存消耗

    哈希表的存储原理方式详解:

    1.调用HashMap的put(K key,V value),传入要存储的键值对数据 2.然后判断此哈希表是否为空的表,如果空则扩容,不空进行下一步 3.获取key值的哈希值(null为0) 4.然后哈希值对哈希表长度取余,得到的即要存储的哈希桶的位置,如果该位置没有数据,则直接插入,如果有数据,下一步 5.判断这个桶中的key是否与新加入的key相等,如果相等,则直接修改value,如果不相等,说明是新键值对,下一步 6.然后此时桶中的数据结构是否是红黑树结构,是则直接插入,不是红黑树则是链表,遍历链表插入,插入后判断链表长度是否>8,大于8则转变成红黑树 7.最后数据插入完毕后,判断哈希表长度决定是否扩容

    哈希表相关类的区别:

    HashMap:线程不安全,效率高HashTable:线程安全,效率低ConcurrentHashMap:采用分段锁机制(即将正在操作的哈希桶范围锁住,不允许同步操作,将未操作的剩余哈希桶范围放开,允许一个新线程进来操作),效率相对较高TreeMap:有顺序(从小到大)二叉树结构存储键值LinkedHashMap:将数据同时存入一个双向链表和HashMap,可以保存存储的顺序

    哈希表类常用的存取代码:

    HashMap<Integer,String> hashMap = new HashMap<Integer, String>(); hashMap.put(1,"这是第一条"); hashMap.put(2,"这是第二条"); hashMap.put(3,"这是第三条"); //获取单个的数据 System.out.println(hashMap.get(2));//这是第二条 /* 获取所有的键和数据(方法1):通过key转换的set集挨个单独获取 */ //先将所有数据的键值转换成set集 Set<Integer> integers = hashMap.keySet(); //对set集进行迭代器遍历输出键值 for(int key:integers){ System.out.print(key+ ",");//1,2,3 System.out.println(); } //对set集进行迭代器遍历输出键值所对应的数据 for(int key:integers){ System.out.print(hashMap.get(key)+ ",");//这是第一条,这是第二条,这是第三条 System.out.println(); } /* 获取所有的键和数据(方法2):通过value转换的collection集直接输出 */ Collection<String> values = hashMap.values(); for(String value:values){ System.out.print(value+ ",");//这是第一条,这是第二条,这是第三条 System.out.println(); }

    都学习到这里了,不妨点击一个关注吧~

    Processed: 0.011, SQL: 8