java基础 集合框架

集合框架

集合的概念

  • 对象的容器,实现了对对象常用的操作方法,可实现数组的功能

  • 和数组的区别:

    (1)数组的长度固定,集合长度不固定

    (2)数组可以存储基本类型和引用类型,集合只能引用类型

Collection接口

无序,无下标,无顺序,重点是它的跌打器Iterator JDK1.8中 增加了一个新的方法 可以直接用lambda 表达式去遍历每一个元素 这样就不用每次判断 是否hasNext();

具体跟Spliterators 接口有关 ,有时间研究一下

forEachRemaining(Consumer<?super E> action) 对每个剩余元素执行给定的操作,直到所有元素都被处理或动作引发异常;

简单使用 这里以List的迭代器为例

List<String> stringList = new ArrayList<>();

stringList.add("one");
stringList.add("two");
stringList.add("three");
stringList.add("four");
stringList.add("five");

stringList.listIterator().forEachRemaining(String -> System.out.println(String +" "));

List的接口与实现类

主要方法

List 有序 有下标 可以重复 两个子类 ArrayList(),LinkedList();

**subList(int fromIndex,int toIndex)**从原有的集合中截取一个新的集合 包含下标为fromIndex的元素 以及fromIndex和toIndex之间的元素,不包含toIndex

重点是list的遍历ListIterator 同Iterator不一样,可以反向遍历,并且替代指定位置的元素(set)

提供的方法有

    • add(E e) 将指定的元素插入列表(可选操作)。
      booleanhasNext() 返回 true如果遍历正向列表,列表迭代器有多个元素。
      booleanhasPrevious() 返回 true如果遍历反向列表,列表迭代器有多个元素。
      Enext() 返回列表中的下一个元素,并且前进光标位置。
      intnextIndex() 返回随后调用 next()返回的元素的索引。
      Eprevious() 返回列表中的上一个元素,并向后移动光标位置。
      intpreviousIndex() 返回由后续调用 previous()返回的元素的索引。
      voidremove() 从列表中删除由 next()previous()返回的最后一个元素(可选操作)。
      voidset(E e)指定的元素替换由 next()previous()返回的最后一个元素(可选操作)。
实现类

ArrayList : 由数组结构实现,查询快,增删慢,线程不安全。

源码分析:

  • DEFAULT_CAPACITY 默认容量 10 但是如果没有向集合中添加元素 容量是0 添加任意一个元素之后 容量是10

  • elementData :存放元素的数组;

  • 底层集合的添加,删除方法都是对数组的操作,源码里有一个方法是数组的扩容grow(),每次扩容后的大小是原数组的1.5倍

LinkedList:双向链表结构实现,增删快,查询慢。

链表的基本概念:**链表是一系列存储数据元素的单元通过指针串联起来形成的,因此每个单元至少有两个作用域,一个域用于存储数据元素,另一个指向其他单元的指针,然后它叫做 节点 **(链表的第一个节点和最后一个节点,分别称为链表的头节点和尾节点)

双向链表:由节点组成,它的每个数据节点中都有两个指针,一个是指向前一个节点和后一个节点

源码分析:

transient int size = 0;     //集合大小

/**
 * Pointer to first node.
 * Invariant: (first == null && last == null) ||
 *            (first.prev == null && first.item != null)
 */
transient Node<E> first;   //头节点

/**
 * Pointer to last node.
 * Invariant: (first == null && last == null) ||
 *            (last.next == null && last.item != null)
 */
transient Node<E> last;    //尾节点

/**
 * Constructs an empty list.
 */
public LinkedList() {     //构造
}

Node节点

private static class Node<E> {
    E item;         //元素
    Node<E> next;   //指向下一个节点
    Node<E> prev;   //指向上一个节点

    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

add() 源码分析

/**
 * Links e as last element.
 */
void linkLast(E e) {
    final Node<E> l = last;
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}

添加第一个元素的时候 创建一个Node节点 既是last 也是 first ,当再添加元素的时候 final Node l = last; l 就等于last

所以final Node newNode = new Node<>(l, e, null) 看一下 Node的构造方法

在这里插入图片描述

所以新节点newNode的上一个节点prev指向的就是last

每新add一个元素 就是last

last = newNode;

尾节点就是newNode

临时变量l不为空,所以**l.next = newNode;**这个时候l是firstNode 所以它的next指向新节点 node 也是lastNode

总结:第一次添加的时候 头尾指向的是一个节点,第二次 。。。n次添加的时候,用一个临时节点=尾节点,尾节点等于新节点同时新节点的prev指向的是临时节点,临时节点不为空,所以它的下一个节点指向的就是尾节点。(有点绕)

至于删除 只需要找到要删除的节点,重新指向一下节点即可;不像数组要重新排列

泛型和工具类

什么是泛型?

把类型明确的工作推迟到创建对象或者调用方法的 时候才去明确的特殊类型。其本质是参数化类型,把类型作为参数传递。常见的形式有泛型类,泛型接口,泛型方法。

为什么需要泛型?

早期Java是使用Object来代表任意类型的,但是向下转型有强转的问题,这样程序就不太安全

首先,我们来试想一下:没有泛型,集合会怎么样

  • Collection、Map集合对元素的类型是没有任何限制的。本来我的Collection集合装载的是全部的Dog对象,但是外边把Cat对象存储到集合中,是没有任何语法错误的。
  • 把对象扔进集合中,集合是不知道元素的类型是什么的,仅仅知道是Object。因此在get()的时候,返回的是Object。外边获取该对象,还需要强制转换

有了泛型以后:

  • 代码更加简洁【不用强制转换】
  • 程序更加健壮【只要编译时期没有警告,那么运行时期就不会出现ClassCastException异常】
  • 可读性和稳定性【在编写集合的时候,就限定了类型】

泛型类

使用泛型可以创建变量,作为方法的参数,以及作为方法的返回值,T不能实例化,泛型不能相互复制

泛型只能使用引用类型;

泛型接口

不能使用静态常量

泛型方法

public static <T> T show(T t){
    return t;
}
泛型好处

提高代码的重用性,防止类型转换异常,提高代码安全性ClassCastException

泛型集合

参数化类型,类型安全的集合,强制集合元素类型一致

优点:编译时检查,访问时不必类型转换,不同泛型之间引用不能互相赋值,泛型不存在多态。

Set接口与实现类

无序,无下标,元素不可重复,允许空值全部继承Collection的方法

  • HashSet

    • 基于HashCode实现元素不可重复,以及存放位置;
    • 当存入元素的哈希码相同时,会调用equals进行确认,如果为true,则拒绝后者存入。
    • 判断元素是否重复基于hashcode和equals方法,所以我们在用hashSet的时候需要重写equals()方法
    • 数组+链表的形式
    • HashSet底层就是利用的HashMap
  • TreeSet 红黑树

    • 基于排列顺序实现元素不可重复
    • 实现了sortedSet接口,对集合元素自动排序
    • 元素对象的类型必须实现Comparable接口,指定排序规则,
    • 通过ComparableTo方法确定是否为重复元素’; 实现Comparable接口重写ComparableTo方法 ;
    • 也可在创建的时候指定比较规则
    TreeSet<User> treeSet = new TreeSet<>(new Comparator<User>() {
        @Override
        public int compare(User o1, User o2) {
            //比较规则
            return 0;
        }
    });
    

Map接口与实现类

  • **HashMap **

    存储任意的键值对,K:无序 无下标,不允许重复 V:无序 无下标 允许重复;

    除了增删查清空的方法之外,重点entrySet() keySet() 返回set集合;

    keySet() 获取所有的key值;

    **entrySet()**获取所有的key value值封装成一个entry (Map.entry)类型 一个entry就是一个键值对;

    默认初始容量16 允许key value 为null;

    数组+链表+红黑树;

    HashMap的key值 通过hashCode和equals方法判定和set一样;

    源码分析:

    1. HashMap刚创建时,table是null,节省空间,当添加第一个元素时,table容量调整为16
    2. 当元素个数大于阈值(16*0.75 = 12)时,会进行扩容,扩容后的大小为原来的两倍,目的是减少调整元素的个数
    3. jdk1.8 当每个链表长度 >8 ,并且数组元素个数 ≥64时,会调整成红黑树,目的是提高效率
    4. jdk1.8 当链表长度 <6 时 调整成链表
    5. jdk1.8 以前,链表时头插入,之后为尾插入

    HashTable

    线程安全,运行效率慢;不允许null作为key或是value

    properties

    • Properties类表示一组持久的属性。 Properties可以保存到流中或从流中加载。 属性列表中的每个键及其对应的值都是一个字符串。
  • SortedMap 接口

    • TreeMap 实现类 元素需要实现Comparable接口 重写比较规则 TreeSet类似

      实现了SortedMap接口(是map的子接口),可以对key自动排序

  • Collections 工具类

在这里插入图片描述

也可以通过Comparator 重写排序规则;

创建安全的集合

在这里插入图片描述

jdk1.8中 集合推出Stream流
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值