栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

java集合笔记

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

java集合笔记

目录

一、类集

二、Collection接口

三、List接口

3.1 ArrayList

3.2 Vector

Vector与ArrayList的区别

3.3 linkedList(了解)

 四、Set接口

4.1 HashSet(散列存放)

比较两个对象是否相同的方法(重点)

4.2 TreeSet(排序的子类)

五、集合的输出(遍历)

5.1 Iterator(重点)

扩展:迭代器的快速失败和安全失败

5.2 ListIterator

 5.3 Enumeration(废弃的接口,了解)

5.4 foreach

六、Map接口

6.1 HashMap

扩展:哈希表和HashMap源码分析

6.2 Hashtable

扩展:HashMap与Hashtable的区别

扩展:ConcurrentHashMap

6.3 TreeMap

6.4 Map集合的输出

扩展:TreeMap和linkedHashMap的区别

扩展:Map集合存储自定义对象


一、类集
  • 类集是java对数据结构成熟的实现,在jdk1.2之后正式引入。所有类集操作的接口或类都在java.util包中
  • java类集结构图如下图所示

  •  类集中最大的几个操作接口:Collection、Map、Iterator

二、Collection接口
  • Collection接口是在整个java类集中保存单值的最大操作父接口,里面每次操作时都只能保存一个对象的数据,此接口定义在java.util包中
  • 接口定义如下
public interface Collection extends Iterable
  • 此接口的常用方法

  •  Collection接口的全部子类或子接口将全部继承以上方法,在开发过程中通常使用的是其子接口:List、Set

三、List接口
  • List是Collection的子接口,也是单值存储的集合,里面的所有内容都是允许重复的
  • List子接口的定义
public interface List extends Collection
  • List接口的扩充方法

  • List接口的常用实现类
    • ArrayList(95%)
    • Vector(4%)
    • linkedList(1%)

3.1 ArrayList
  • ArrayList是List接口的子类,定义如下
public class ArrayList extends AbstractList
implements List, RandomAccess, Cloneable, Serializable
  •  顺序存储,存储的内容是有序的
    • 因为底层是用数组实现的
  • ArrayList集合对元素的增删慢,查找快,日常开发中常用于数据查询、遍历数据
  • 简单的源码解析
    • 在调用无参构造器的时候,构造器仅仅是构造了一个空的数组,容量为0
      • 也可以通过有参构造器指定集合的初始容量
    • 在第一次调用add方法时会初始化一个容量为10的空数组
      • add方法中会判断当前数组长度,与数组存储元素的个数是否相同,相同说明数组满了,需要进行扩容操作
    • 扩容操作就是对数组的长度进行1.5倍扩容,其中还有一些其他的判断细节,如判断是否超过int的最大范围、是否为空数组等
    • 具体的源码分析见下面网址

ArrayList源码分析_future_god_qr的博客-CSDN博客

3.2 Vector
  • Vector也是List接口的子类,此类定义如下
public class Vector extends AbstractList
implements List, RandomAccess, Cloneable, Serializable

Vector与ArrayList的区别
区别点ArrayListVector
时间是新的类,在jdk1.2之后推出是旧的类,在jdk1.0时就有
性能性能较高,采用了异步处理,线程不安全性能较低,采用了同步处理,线程安全
输出支持Iterator、ListIterator输出除了支持Iterator、ListIterator输出,还支持Enumeration输出

3.3 linkedList(了解)
  • 此类用的非常少,定义如下
public class linkedList extends AbstractSequentialList
implements List, Deque, Cloneable, Serializable
  • 底层使用的是双向链表结构,增删快,查找慢
    • 可以把linkedList当成栈或队列结构使用,开发时用的比较少,了解即可
  • 此类继承了AbstractList,所以是List的子类;但此类也是Queue接口的子类,Queue接口定义了如下方法

 四、Set接口
  • Set接口也是Collection的子接口,与List接口最大的不同在于,Set接口里面的内容是不允许重复的
  • Set接口没有对Collection接口进行扩充,因此没有List接口中定义的get(int index)方法,所以Set无法通过循环获取数据
    • 但是可以通过迭代器获取数据
    • 或者使用toArray方法变成数组后再遍历获取数据,toArray方法最好指定泛型,因为Set在操作的时候已经指定了数据类型
  • Set接口的常用子类有两个:HashSet、TreeSet

4.1 HashSet(散列存放)
  • 因为Set接口没有扩充任何Collection接口中的方法,所以方法都是Collection中定义的
  • HashSet属于散列的存放类集,里面的内容都是无序存放的
  • 底层是使用HashMap实现的,即使用了HashMap的key来实现HashSet,后面讲解map的时候会阐述
    • 底层存放数据的数据结构是哈希表(散列表)
      • jdk1.8后哈希表底层采用数组+链表+红黑树实现

比较两个对象是否相同的方法(重点)
  • 有两种方法
    • 第一种:判断两个对象的编码是否一致,这个方法需要通过hashCode()完成,即:每个对象有唯一的编码
    • 第二种:如果还需要进一步验证对象中的每个属性是否相等,还需要通过equals完成。所以此时需要重写Object类中的hashCode()方法,此方法表示一个唯一的编码,一般是通过公式计算出来的
      • 即equals和hashCode方法都重写
  • 举例
public boolean equals(Object obj) {
    if (this == obj) {
        return true;
    } 
    if (!(obj instanceof Person)) {
        return false;
    } 
    Person per = (Person) obj;
    if (per.name.equals(this.name) && per.age == this.age) {
        return true;
    } else {
        return false;
    }
} 

public int hashCode() {
    return this.name.hashCode() * this.age;
}
  • 写这个知识点,是因为后面的TreeSet判断对象是否相同,是通过Comparable或者Comparator间接完成的,在HashSet中添加实现Comparable的对象,仍然存在我们认为的相同对象(属性值都相同)依然可以添加进set集合的情况
    • 这种情况下,需要真正实现重复元素的判断,需要重写equals和hashCode方法

4.2 TreeSet(排序的子类)
  • 与HashSet不同的是,TreeSet本身属于排序的子类,此类定义如下
public class TreeSet extends AbstractSet
implements NavigableSet, Cloneable, Serializable
  • 底层存储数据的数据结构是二叉树,因此结果是有序的
    • 有序指的是按一定规则排列后的顺序,而不是输入的顺序
    • 底层是基于TreeMap实现的
  • TreeSet存储的数据是需要有比较规则的,有两个方式实现
    • 方式一:传入TreeSet的数据实现Comparable接口,实现compareTo方法(判断大小的逻辑方法)
      • 升序排列
        • this小返回负数,一样大返回0,this大返回正数
      • 降序排列
        • this小返回正数,一样大返回0,this大返回负数
      • 返回0说明是同一个对象,无法重复添加进set集合
    • 方式二:TreeSet传入一个比较器Comparator,在比较器里面重写compare方法
      • 该方法的判断逻辑和方式一一致,只不过this变成了传入的第一个参数o1,被比较的是第二个参数o2
      • 可以使用匿名内部类的方式实现Comparator,再重写compare方法

五、集合的输出(遍历)
  • 对于集合的输出(遍历),有以下几种方式
    • Iterator迭代输出(90%)
      • 用于List和Set,不能用于Map
      • 迭代效率最高
    • ListIterator(5%)
      • 只能用于迭代List
    • Enumeration(1%)
    • foreach(4%)
      • 迭代集合时本质还是使用Iterator

5.1 Iterator(重点)
  • Iterator属于迭代输出
  • 原理
    • 不停判断是否有下一个元素,有的话就输出
  • 接口定义
public interface Iterator
  • 要想使用Iterator接口,则必须使用Collection接口。在Collection接口中规定了一个iterator()方法,可以用于为Iterator接口进行实例化操作
  • 接口定义的方法

  •  Iterator中的操作指针在第一条指针之上,当调用next()方法时,获取当前指针指向的值并向下移动,使用hasNext()可以检查序列中是否还有元素

  •  在进行迭代输出的时候,如果想要删除当前元素,只能使用Iterator接口中的remove方法,不能使用集合中的remove方法,否则将出现未知的错误。
    • 集合中很少有删除元素的操作,所以可以忽略这一点
  • 此接口只能进行从前到后的单向输出,如果要进行双向输出,必须使用其子接口ListIterator
  • 举例
package org.listdemo.iteratordemo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class IteratorDemo02 {
    public static void main(String[] args) {
        Collection all = new ArrayList();
        all.add("A");
        all.add("B");
        all.add("C");
        all.add("D");
        all.add("E");
        Iterator iter = all.iterator();
        while (iter.hasNext()) {// 判断是否有下一个元素
            String str = iter.next(); // 取出当前元素
            if (str.equals("C")) {
                all.remove(str); // 错误的, 调用了集合中的删除
            } else {
                System.out.print(str + "、 ");
            }
        }
    }
}

扩展:迭代器的快速失败和安全失败
  • 集合在进行迭代的时候都存在迭代器的快速失败和安全失败
    • 失败的含义
      • 遍历过程中数据发生了改变,最后遍历的结果和最初的预期不一定是一样的,可能会发生错误,这就叫失败
  • 快速失败
    • 迭代器遍历的是集合本身,迭代的时候会有一个记录自己遍历到哪一个数据的变量。如果迭代器在遍历的时候,有另外一条执行路径对集合内容进行了改变(比如删了几个元素,造成元素个数的改变),这个时候迭代器遍历到后面被操作的数据时可能会出错(比如原本十个数据,遍历的过程删到只剩五个数据,遍历的时候可能就会出问题)
      • 这个时候迭代器就会抛出ConcurrentModificationException异常
    • 简单来说,就是遍历的过程中集合发生了改变,就会发生快速失败
  • 安全失败
    • 安全失败指的是发生失败情况的时候,遍历的数据不会出错
      • 安全失败是在遍历集合之前,将集合复制了一份,遍历的是复制的集合
        • 此时遍历结果与原集合是否被操作无关
    • 一般情况下集合都是安全失败的,具体要看API文档的描述

5.2 ListIterator
  • ListIterator是可以进行双向输出的迭代接口,是Iterator的子接口,定义如下
public interface ListIterator
extends Iterator
  • 此迭代器只能用于List集合的遍历,不能用于Set
  • 此接口扩展了以下操作方法

 5.3 Enumeration(废弃的接口,了解)
  • Enumeration 是一个非常古老的输出接口, 其也是一个元老级的输出接口, 最早的动态数组使用 Vector 完成, 那么只要是使用了 Vector 则就必须使用 Enumeration 进行输出。
     

5.4 foreach
  • foreach可以用来输出数组的内容,也可以输出集合中的内容
    • 集合只能是Collection,即List和Set
  • 在使用foreach时需要注意,里面的操作泛型要指定具体的类型,这样在输出的时候才会更加有针对性
  • foreach又叫增强for循环,在迭代集合的时候本质还是使用迭代器来进行迭代
  • 语法格式
for(数据类型 变量 : 集合或数组名){
  操作
}

六、Map接口
  • Map是双值存储的集合,集合中存储的是一个个键值对数据(将键映射到值的对象)
    • 与Collection没有关系,是第二大的集合操作接口
  • Map集合不能包含重复的键(key),每个键最多可以映射一个值(value),值可以重复
  • Map本身是一个接口,常用的子类有:HashMap、TreeMap、Hashtable
  • 常用API
    • 注意map转为set和Collection的方法

6.1 HashMap
  • HashMap是Map的子类,定义如下
public class HashMap extends AbstractMap
implements Map, Cloneable, Serializable
  • HashMap本身是属于无序存放的
  • 底层数据结构是哈希表
  • key也可以是null,但是只能有一个null

扩展:哈希表和HashMap源码分析

HashMap源码分析_future_god_qr的博客-CSDN博客

6.2 Hashtable
  • Hashtable是最早的key-value的操作类,在jdk1.0的时候推出,基本操作与HashMap类似。
  • Hashtable中不可以插入null值

扩展:HashMap与Hashtable的区别
区别HashMapHashtable
推出时间jdk1.2之后推出的新的操作类jdk1.0时推出的旧的操作类
性能异步处理,性能较高,线程不安全同步处理,性能较低,线程安全
能否设置null允许设置为null不允许设置,否则将出现空指针异常

扩展:ConcurrentHashMap
  • jdk1.7时推出的一种数组+分段锁实现的map集合
    • 采用分段锁机制,保证了线程安全,效率比较高(没有HashMap高)
  • 操作同一个桶的时候才需要排队,操作的如果不是同一个桶,则不需要排队

6.3 TreeMap
  • TreeMap子类是允许key进行排序的操作子类,其本身在操作的时候将按照key进行排序,另外,key中的内容可以为任意对象,但是要求对象所在的类必须实现Comparable接口
  • 在使用Map接口的时候并不关心其是否排序,所以此类只需要知道其特点即可

6.4 Map集合的输出
  • Map接口没有iterator()方法的定义,所以Map接口本身是不能直接使用Iterator进行输出的
  • 如果非要使用Iterator对Map集合进行输出,可以采取以下步骤
    • 使用 Map 接口中的 entrySet()方法将 Map 接口的全部内容变为 Set 集合
    • 可以使用 Set 接口中定义的 iterator()方法为 Iterator 接口进行实例化
    • 之后使用 Iterator 接口进行迭代输出, 每一次的迭代都可以取得一个 Map.Entry 的实例
    • 通过 Map.Entry 进行 key 和 value 的分离
  • Map.Entry 本身是一个接口。 此接口是定义在 Map 接口内部的, 是 Map 的内部接口。 此内部接口使用 static 进行定义,所以此接口将成为外部接口
    • 实际上来讲, 对于每一个存放到 Map 集合中的 key 和 value 都是将其变为了 Map.Entry 并且将 Map.Entry 保存在了Map 集合之中
  • Map.Entry接口的常用方法

  •  此外,还可以使用foreach+lambda表达式实现Map集合的输出

扩展:TreeMap和linkedHashMap的区别
  • TreeMap
    • 使用平衡二叉树存储
    • 存储的数据是有序的
      • 不是按存储的顺序,而是按key实现的的Comparable接口中定义的排序规则
  • linkedHashMap
    • 额外使用了一个双向链表存储数据
      • 存储的数据既在哈希表中,又在链表中
        • 链表维护存储的顺序,按存存储的顺序排序

扩展:Map集合存储自定义对象
  • 自定义的对象一定要支持equals和hashCode方法
    • 如果没重写这两个方法,默认使用的是Object的方法,性能不高
  • 如果自定义的对象作为Map的key值存储,存储之后不能随便修改对象的属性值
    • 如果需要改变对象的内容,就不要存储在key的位置
    • 一旦改变了key的对象的值,很容易造成hash值错乱的问题

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/489390.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号