Java学习笔记——Java 集合概述


一、Java集合概述

  • 数组元素既可以是基本类型的值,也可以是对象;
  • 集合里只能保存对象
  • Collection 集合体系的继承树:
    Set:无序集合,元素不可重复;
    List:有序集合,元素可以重复;
    在这里插入图片描述
  • Map 体系的继承树:
    Map 保存的数据都是以 key-value 对的形式保存;
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

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

  • Java 的集合可以分为三大类
    (1)Set 集合;只能根据元素本身来访问,元素不能重复;
    (2)List 集合;可以根据索引来访问;
    (3)Map 集合;可以根据 key 来访问 value;

二、Collection 和 Iterator 接口

  • collection 集合的操作:
public class CollectionTest {
    public static void main(String[] args) {
        Collection c = new ArrayList();
        c.add("zzz");
        c.add(6);
        c.remove(6);
        System.out.println("c集合的元素个数为:" + c.size());
        System.out.println("是否包含字符 zzz :" + c.contains("zzz"));

        Collection books = new HashSet();
        books.add("book1");
        books.add("book2");
        System.out.println("c集合是否完全包含books集合:" + c.containsAll(books));
    }
}

1. 使用 Lambda 表达式遍历集合
2. 使用 java8 增强的 Iterator 遍历集合元素
  • Iterator 接口是 Java 集合框架的成员,Iterator 主要用于遍历 Collection 中的元素,Iterator 对象也被称为迭代器;
  • 注意:Iterator 仅用于遍历集合,本身并不提供盛装对象的能力;
public class IteratorTest {
    public static void main(String[] args) {
        Collection c = new ArrayList();
        c.add("dd");
        c.add("520");
        c.add("zz");
        c.add("725");

        //获取集合对应的迭代器
        Iterator it = c.iterator();
        while (it.hasNext()){
            //it.next() 返回值为为 Object 类型,需要强制类型转换
            String s = (String) it.next();
            System.out.println(s);

            //删除 Iterator 中的元素
//            if (s.equals("725")){
//                it.remove();
//            }

            s = "haha";
        }
        System.out.println(c);
    }
}

//输出结果为:
dd
520
zz
725
[dd, 520, zz, 725]
  • 当使用 Iterator 对集合进行迭代时,Iterator 并不是把集合元素本身传给了迭代变量,而是把集合元素的值传递给了迭代变量,所以修改迭代变量的值对集合元素本身并没有任何影响
  • 当使用 it.remove(); 语句进行删除时,集合元素会发生改变;
3. 使用 Lambda 表达式遍历 Iterator
Iterator it = books.Iterator();
it.forEachRemaining(obj -> System.out.println("迭代集合元素:" + obj));
4. 使用 foreach 循环遍历集合
for (Object obj : books){
	String book = (String) obj;
	System.out.println(book);
}
5. 使用 java8 新增的 Predicate 操作集合
6. 使用 java8 新增的 Stream 集合
  • Stream 是一个通用流接口;IntStream、LongStream 、DoubleStream 则代表元素类型值为 int、long、double 的流;
  • 对于大部分聚集方法而言,每个 Stream 只能执行一次;
public class IntStreamTest {
    public static void main(String[] args) {
        IntStream s = IntStream.builder()
                .add(5)
                .add(2)
                .add(0)
                .add(7)
                .build();
        //下面调用聚集方法的代码 每次只能执行一次
//        System.out.println("max = " + s.max().getAsInt());
        System.out.println("min = " + s.min().getAsInt());
//        System.out.println("sum = " + s.sum());
//        System.out.println("average = " + s.average());
    }
}

  • 上面的注释部分代码,每次只能调用一行;

三、Set 集合

  • Set 集合通常不能记住元素的添加顺序;Set 集合与Collection 基本相同,没有提供任何额外的方法;
  • Set 集合不允许包含相同的元素;
1. HashSet 类
  • HashSet 是 Set 接口的典型实现;HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取和查找性能;

  • HashSet 具有以下特性
    ①不能保证元素的排列顺序;(无序)
    ②HashSet 不是同步的,如果多个线程同时访问一个 HashSet,假设有两个或两个以上线程同时修改了 HashSet 集合,则必须通过代码来保证其同步;
    ③集合元素值可以是 null;

  • HashSet 判断两个元素相等的标准:两个对象通过 equals() 方法比较相等,并且两个对象的 hashCode() 方法返回值也相等;

  • 注意:当把一个对象放入 HashSet 时,如果需要重写该对象对应类的 equals() 方法,则也应该重写其 hashCode() 方法;规则是:如果两个对象 equals() 方法返回值为 true,这两个对象的 hashCode 值也应该相同;

  • 重写 hashCode() 方法的基本规则
    ①在程序运行过程中,同一个对象多次调用 hashCode() 方法应该返回相同的值;
    ②当两个对象通过 equals() 方法比较返回 true 时,这两个对象的 hashCode() 方法应返回相等的值;
    ③对象中用作 equals() 方法比较标准的实例变量,都应该用于计算 hashCode 值;

  • 重写 hashCode() 方法的一般步骤

class R{
    int count;
    public R(int count){
        this.count = count;
    }
    public String toString(){
        return "R[count: " + count + "]";
    }
    public boolean equals(Object obj){
        if (this == obj){
            return true;
        }
        if (obj != null && obj.getClass() == R.class){
            R r = (R) obj;
            return this.count == r.count;
        }
        return false;
    }
    public int hashCode(){
        return this.count;
    }
}

public class HashCodeTest {
    public static void main(String[] args) {
        HashSet hs = new HashSet();
        hs.add(new R(5));
        hs.add(new R(-3));
        hs.add(new R(9));
        hs.add(new R(-2));
        System.out.println(hs);

        //取出第一个元素
        Iterator it = hs.iterator();
        R first = (R) it.next();

        //为第一个元素的 count 实例变量赋值
        first.count = -3;
        System.out.println(hs);

        //删除 count 值为-3 的R对象
        hs.remove(new R(-3));
        System.out.println(hs);

        //判断是否包含
        System.out.println("是否包含-3:" + hs.contains(new R(-3)));
        System.out.println("是否包含-2:" + hs.contains(new R(-2)));
        System.out.println("是否包含5:" + hs.contains(new R(5)));
    }
}
//输出结果:
[R[count: -2], R[count: -3], R[count: 5], R[count: 9]]
[R[count: -3], R[count: -3], R[count: 5], R[count: 9]]
[R[count: -3], R[count: 5], R[count: 9]]
是否包含-3false
是否包含-2false
是否包含5true
  • 由以上代码可以发现:当程序把可变对象添加到 HashSet 中之后,尽量不要去修改集合元素中参与计算 hashCode()、equals() 的实例变量,否则将会导致 HashSet 无法正确操作这些集合元素;
2. LinkedHashSet 类
  • LinkedHashSet 是 HashSet 的子类,但它使用链表维护元素的次序;当遍历 LinkedHashSet 集合里的元素时,将会按照元素的添加顺序来访问集合里的元素(有序);
  • 由于 LinkedHashSet 需要维护元素的顺序,所以性能略低于 HashSet;
  • LinkedHashSet 具有唯一性有序性元素的顺序总是与添加顺序一致
3. TreeSet 类
  • TreeSet 是 SortSet 接口的实现类,可以确保元素处于排序状态;
  • 元素的顺序不是根据添加顺序,而是根据元素实际值的大小进行排序的
  • TreeSet 采用红黑树的数据结构来存储集合元素;
  • TreeSet 支持两种排序方法:自然排序和定制排序,默认情况下,用自然排序;
(1)自然排序
  • TreeSet 会调用集合元素的 compareTo(Object obj) 方法来比较元素之间的大小关系,然后将集合元素按升序排列
  • compareTo(Object obj) 方法返回一个整数值,实现 Comparable 接口的类必须实现该方法;
  • 向 TreeSet 中添加的应该是同一个类的对象,否则会引发 ClassCastException 异常;
  • 对于 TreeSet 而言,它判断两个对象是否相等的唯一标准是:两个对象通过 compareTo(Object obj) 方法比较是否返回0,为0则说明相等;
  • 注意:当需要把一个对象放入 TreeSet 中,重写该对象对应类的 equals() 方法时,应遵循以下规则:如果两个对象通过 equals() 方法比较返回 true 时,这两个对象通过 compareTo(Object obj) 方法比较应返回 0;
  • 一但改变了 TreeSet 集合里可变元素的实例变量,当再试图删除该对象时,TreeSet 也会删除失败;可以删除没有被修改的实例变量、且不与其他被修改的实例变量的对象重复的对象;
(2)定制排序
  • 实现定制排序,则需要在创建 TreeSet 集合对象时,提供一个 Comparator 对象与该 TreeSet 集合关联;
4. EnumSet 类
  • EnumSet 中所有元素都必须是指定枚举类型的枚举值,该枚举值在创建 EnumSet 时显式或隐式的指定;
  • EnumSet 的集合元素是有序的,EnumSet 以枚举值在 Enum 类内的定义顺序来决定集合元素的顺序;
  • EnumSet 在内部以位向量的形式存储,这种存储形式非常紧凑、高效,因此占内存很小,而且运行效率很好,执行批量操作速度非常快
  • EnumSet 集合不允许加入 null 元素
5.各 Set 实现类的性能分析
  • HashSet 和 TreeSet 是 Set 的两个典型实现,HashSet 的性能总是比 TreeSet 好(因为 TreeSet 需要红黑树算法来维护);只有当需要一个保持排序的 Set 时,才应使用 TreeSet ,否则都应使用 HashSet;
  • HashSet 还有一个子类:LinkedHashSet ,对于普通插入删除,LinkedHashSet 比 HashSet 一点(需要维护链表),但遍历 LinkedHashSet 较
  • EnumSet 是所有Set中性能最好的,但只能保存枚举值;
  • HashSet 、TreeSet、EnumSet 都是线程不安全的;如果有多个线程同时访问同一个 Set 集合,并且有超过一个线程修改了该 Set 集合,则必须手动保证该 Set 集合的同步性;

四、List 集合

1. Java8 改进的 List 接口和 LIstIterator 接口
  • LIst 集合 代表一个元素有序、可重复的集合,集合中的每个元素都有其对应的顺序索引

  • List 是 Collection 接口的子接口;

  • 与 Set 集合相比,List 增加了根据索引来插入、替换和删除集合元素的方法;

  • List 判断两个对象相等,只要通过 equals() 方法比较返回 true 即可;

  • List 还额外提供了一个 listIterator() 方法,该方法返回一个 ListIterator 对象;ListIterator 接口继承了 Iterator 接口,提供了专门操作 List 的方法;

  • ListIterator 与普通的 Iterator 相比,ListIterator 增加了向前迭代的功能( Iterator 只有向后迭代);ListIterator 还可向 List 集合中添加元素( Iterator 只能删除元素);

2. ArrayList 和 Vector 实现类
  • ArrayList 和 Vector 作为 List 类的两个典型实现,支持前面介绍的 List 接口的功能;
  • ArrayList 和 Vector 类都是基于数组实现的 List 类,所以 ArrayList 和 Vector 类封装了一个动态的、允许再分配的 Object[ ] 数组
  • ArrayList 和 Vector 对象使用 initialCapacity 参数来设置数组的长度;
  • 如果开始就知道 ArrayList 或 Vector 集合需要保存的元素个数,则可以在创建它们时就指定 initialCapacity 的大小;如果创建空的 ArrayList 或 Vector 集合时不指定 initialCapacity 参数,则 Object[] 的默认大小为 10
  • Vector 是一个比较老的集合,缺点也较多,尽量少使用 Vector 实现类;
  • ArrayList 和 Vector 的区别
    ①ArrayList 是线程不安全的;后面提供一个 Collections 工具类,可以将一个 ArrayList 变成线程安全的;
    ②Vector 是线程安全的;性能较低;不推荐使用;
    注意:Vector 还提供了一个 Stack 子类,它用于模拟“栈”这种数据结构。但它也是比较早的集合类,线程安全、性能较差,不推荐使用;如果程序需要使用“”这种数据结构,则可以考虑使用 ArrayDeque
3. 固定长度的 List
  • Arrays 是一个操作数组的工具类,该工具类里提供了 asList(Object… a) 方法,该方法可以把一个数组或指定个数的对象转换成一个 List 集合;
  • 这个集合是 Arrays 的内部类 ArrayList 的实例;
List li = Arrays.asList("dd", "zz");
//获取li的实现类,输出类型:Arrays$ArrayList
System.out.println(li.getClass());
//使用方法引用遍历集合元素
li.forEach(System.out::plintln);
五、Queue 集合
  • Queue 用于模拟“队列”;
  • Queue 有一个 PriorityQueue 实现类,还有一个 Deque 接口;
  • Deque 代表一个“双端队列”,因此它的实现类既可以当成队列使用,也可以当成使用;
1. PriorityQueue 实现类
  • PriorityQueue 是一个比较标准的队列实现类,但它保存队列元素的顺序并不是按加入队列的顺序,而是按队列元素的大小进行重新排序
  • PriorityQueue 不允许插入 null 值;PriorityQueue 的元素有两种排序方式:自然排序和定制排序;(PriorityQueue 队列对元素的要求与 TreeSet 对元素的要求基本一致);
2. Deque 接口与 ArrayDeque 实现类
  • Deque 接口 是 Queue 接口的子接口,它代表一个双端队列,也可以当成来使用;
  • Deque 接口提供了一个典型的实现类:ArrayDeque,它是一个基于数组实现的双端队列;创建 Deque 时可以指定一个 numElements ,用于指定数组长度,不指定则默认16;
3. LinkedList 实现类
  • LinkedList 类是 List 接口的实现类,还实现了 Deque 接口;
  • LinkedList 可以作为 List集合、双端队列、栈使用;
  • LinkedList 与 ArrayList、ArrayQueue 的实现机制完全不同;
    ArrayList、ArrayQueue 内部以数组的形式来保存,随机访问集合元素时性能较好;
    LinkedList 内部以链表的形式来保存集合中的元素,随机访问性能较差,但插入、删除性能较好;
  • 对于所有的内部基于数组的集合实现,例如 ArrayList、ArrayQueue 等,使用随机访问的性能比使用 Iterator 的性能好
4. 各种线性表的性能分析
  • List线性表接口,
    ArrayList 是基于数组的线性表,
    LinkedList 是基于的线性表,
    Queue 代表队列
    Deque 代表了双端队列
  • 总体来说,ArrayList 的性能比 LinkedList 性能好;
  • 使用 List 集合
    ①如果需要遍历 List 集合元素,
    ArrayList、Vector 应采用随机访问方法(get)遍历集合;
    LinkedList 应采用迭代器(Iterator)来遍历集合;
    ②如果需要经常执行插入、删除操作,可考虑使用 LinkedList;
    ③如果有多个线程需要同时访问 List 集合中的元素,可考虑使用 Collections 将集合包装成线程安全的集合;

六、Java8 增强的 Map 集合

  • Map 用于保存具有映射关系的数据,key 和 value 之间存在一对一关系,key 不能重复;
  • Map 里 key 集和 Set 集合里元素的存储形式很像,value 集和 List 集合很像;
  • Map 有时也被称为字典,或关联数组
  • Map 中包括一个内部类 Entry,该类封装了一个 key-valuea 对;
1. Java8 为 Map 新增的方法
2. Java8 改进的 HashMap 和 Hashtable 实现类
  • HashMap 和 Hashtable 都是 Map 接口的典型实现类,他们之间的关系完全类似于 ArrayList 和 Vector;Hashtable 是一个很早的 Map 实现类;
  • HashMap 和 Hashtable 区别
    ①HashMap 在存在 key 冲突时依然具有较好的性能;
    ②Hashtable 是一个线程安全的 Map 实现,HashMap 不安全,HashMap 性能较高
    Hashtable 不允许使用 null 作为 key 和 value,HashMap 可以使用 null 作为 key 或 value;
  • HashMap 和 Hashtable 不能保证 key-value 对的顺序;
  • HashMap 和 Hashtable 判断两个 value 相等的标准:两个对象通过 equals() 方法比较返回 true 即可;
3. LinkedHashMap 实现类
  • HashSet 有一个 LinkedHashSet 子类,HashMap 也有一个 LinkedHashMap 子类;LinkedHashMap 使用双向链表来维护 key-value 对的次序,迭代顺序与 key-value 对的插入顺序一致
4. 使用 Properties 读写属性文件
  • Properties 类是 Hashtable 类的子类,Properties 类可以把 Map 对象和属性文件关联起来,从而将 Map 对象中的 key-value 对写入属性文件中
  • Properties 相当于一个 key 和 value 都是 String 类型的 Map;
  • Properties 可以把 key-value 对以 XML 文件的形式保存起来,也可以从 XML 文件中加载 key-value 对;
5. SortedMap 接口和 TreeMap 实现类
  • SortedMap 接口也有一个 TreeMap 实现类;
  • TreeMap 就是一个红黑树数据结构,每个 key-value 对即作为红黑树的一个节点,存储时需要根据 key 对节点进行排序;
  • TreeMap 中判断两个 key 相等的标准是:两个 key 通过 compareTo() 方法返回0;
6. WeakHashMap 实现类
7. IdentityHashMap 实现类
8. EnumMap 实现类
9. 各 Map 实现类的性能分析
  • HashMap 通常比 Hashtable 快;
  • TreeMap 通常比 HashMap、Hashtable 慢(尤其在插入、删除 key-value 对时更慢);
  • TreeMap 中的 key-value 对总是处于有序状态;
  • 对于一般的应用场景,应多考虑使用 HashMap,因为 HashMap 正是为快速查询设计的;

七、HashSet 和 HashMap 的性能选项

八、操作集合的工具类:Collections

  • Java 提供了一个操作 Set、List、Map 集合的工具类:Collections;
1. 排序操作
2. 查找、替换操作
3. 同步控制
  • Collections 类中提供了多个 synchronizedXxx() 方法,该方法可以将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题;
4. 设置不可变集合
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值