Java(6)集合

一、Java集合框架概述

1、什么是集合

  • 集合框架:用于存储数据的容器。
    • 数组、集合等存储数据的结构,叫Java容器
    • 此时的存储,是指内存层面的存储,不涉及持久化的存储。
  • 任何集合框架都包含三大块的内容:对外的接口、接口的实现、对集合运算的算法。

2、集合的特点

  • 数组的特点/缺点

    • 长度固定。一旦初始化,长度不能修改。
    • 类型确定。类型严格(算是一个好处),当然要想可以放多种类型的数据,也可以声明为Object类型。
    • 方法有限。添加、删除元素效率低;获取元素个数不方便。
    • 元素有序可重。对于无序、不重复的元素,不能满足。
  • 集合的特点/优点

    • 容量自增长。

3、集合的体系

  • Collection接口Map接口是所有集合框架的父接口。
  • Collection接口继承树

说明:

  1. List接口:存储有序、可重复的数据。“动态数组”
  2. Set接口:存储无序、不可重复的数据。“数学中的集合”
  • Map接口继承树

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hrqxUtNv-1606054949628)(https://img2020.cnblogs.com/blog/2031463/202011/2031463-20201122164513542-2144379932.png)]

说明:

  1. Map接口:双列集合,用于存储成对的数据(key-value)。“数学中的函数”

二、Collection接口中的方法(15个)

1、说明

  • 因为Collection接口是List、Set、Queue接口的父接口,所以定义在Collection接口的中方法可以用于操作子接口的实现类的对象。

2、15个方法

方法描述
add(Object obj)添加元素
addAll(Collection c)添加另一个集合中所以元素
size()元素个数
clear()清空集合
isEmpty()是否为空
contains(Object obj)包含某个元素
containsAll(Collection c)包含某个集合中所有元素
remove(Object obj)删除元素
removeAll(Collection c)差集
retainAll(Collection c)交集
equals(Object obj)判断集合是否相等
toArray()转换为数组
toArray(T[] a)
hashCode()求集合的哈希值
iterator()返回迭代器对象

注意事项:

  • 想Collection接口的实现类对象中添加数据obj时,要求obj所在类重写equals()

  • 数组—>集合:Arrays.asList()

    •   List arr1 = Arrays.asList(new int[]{123, 456});
        System.out.println(arr1.size());//1
      
    •   List arr2 = Arrays.asList(new Integer[]{123, 456});
        System.out.println(arr2.size());//2
      
    • asList中是整型时,用int,默认会把数组当场一个对象。

3、Iterator迭代器接口

  • Iterator是什么?
    • Iterator对象成为迭代器,是设计模式的一种。主要用于遍历集合中的元素。
    • 迭代器模式:提供一种方法访问一个容器(container)对象中各个元 素,而又不需暴露该对象的内部细节。迭代器模式,就是为容器而生。
  • Iterator接口中方法
    • hasNext()
    • next()
    • remove()遍历过程中移除某元素
      • Iterator可以删除集合的元素,但是是遍历过程中通过迭代器对象的 remove方 法,不是集合对象的remove方法。
      • 如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove 方法, 再调用remove都会报IllegalStateException
  • 怎么用Iterator遍历集合
//用集合中的iterator方法得到迭代器对象
Iterator iterator = coll.iterator();
//遍历集合
while(iterator.hasNext()){
    System.out.println(iterator.next());
}

//当然我们也可以用foreach来遍历,其实foreach本质也是调用了迭代器。
  • Iterator原理

    • 得到的迭代器对象,默认指针指向第一个元素的前面
    • hasNext()来判断下一位是否有元素。(在调用it.next()方法之前必须要调用it.hasNext()进行检测。若不调用,且 下一条记录无效,直接调用it.next()会抛出NoSuchElementException异常)
    • next():两个作用:①指针下移②取出指针指向的元素
  • 注意

    • 集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合 的第一个元素之前。因此下面这种写法不正确
while (coll.iterator().hasNext()){
    System.out.println(coll.iterator().next());
}

三、List接口

1、List接口概述

  • List接口是Collection的子接口
  • List被称为“动态数组”,是因为数组的局限性而代替数组的一种容器。
  • 存储的元素有序、可重复。每个元素都有对应的顺序索引
  • 实现类有三个:ArrayList、LinkedList、Vector

2、ArrayList、LinkedList、Vector的异同(面试题)

    • 三个类都是List接口的实现类,因此存储数据都是有序可重复的。
实现类地位since线程安全否底层应用场景
ArrayList主要实现类1.2线程不安全、效率高Object[]遍历、查找
LinkedList1.2双向链表频繁插入、删除
Vector古老实现类1.0线程安全、效率低Object[]

3、源码分析(加分项)

  • ArrayList源码分析-----JDK7

    • 创建:调用空参构造器时,底层创建一个长度为10的Object[]数组elementData
    • 扩容:默认扩容为原来的1.5倍(使用移位操作),并将原来数组中的数据复制到新的数组中
    • 结论:开发中使用带参构造器指定容量:ArrayList list = new ArrayList(int capacity)
  • ArrayList源码分析-----JDK8

    • 创建:调用空参构造器时,底层创建一个Object[]数组elementData,初始化为空{}
    • 扩容:同JDK7
    • 对比:jdk7中的ArrayList的对象的创建类似于单例的饿汉式,而jdk8中的ArrayList的对象的创建类似于单例的懒汉式,延迟了数组的创建,节省内存
  • LinkedList源码分析

    • 创建:调用空参构造器时,内部声明了Node类型的first和last属性,默认值为null
    • 添加:将数据封装在Node中,创建Node对象
  • Vector源码分析

    • 创建:调用空参构造器时,底层创建一个长度为10的Object[]数组elementData
    • 扩容:默认扩容为原来的2倍,并将原来数组中的数据复制到新的数组中

4、List常用方法

  • List除了从Collection集合继承的方法外,还有一些独有的、根据索引来操作集合元素的方法

  • 总结的简化版(常用的、便于记忆的总结)

常用方法的作用方法名
add(Object obj)
remove(Object obj)、remove(int index)
set(int index, Object obj)
get(int index)
add(int index, Object obj)
长度size()
遍历①、Iterator迭代器
②、增强for循环
③、普通for循环
  • 区分List中的remove方法
    • list.remove(2):删除索引为2的,因为有remove(int index)的方法,直接匹配上不用自动装箱就能匹配,name何必自动装箱呢?
    • list.remove(new Integer(2)):删除数据2,匹配的是remove(Object obj)的方法

四、Set接口

1、Set接口概述

  • Set接口是Collection的子接口。
  • Set中没有额外的方法
  • Set中的元素不可重复,判断是否相等用的是equals()方法
  • 实现类有HashSet、LinkedHashSet、TreeSet

2、HashSet、LinkedHashSet、TreeSet的异同

  • HashSet:是Set接口的主要实现类;线程不安全;可以存储null值;
  • LinkedHashSet:是HashSet的子类;可以按照添加顺序遍历,遍历效率高;
  • TreeSet:可以按照添加对象的指定属性排序
    • 要求1:添加的对象类型相同
    • 要求2:对象的类要实现Comparable接口来进行自然排序,或者,实现Comparator接口来进行定制排序
      • 自然排序中,比较两个对象是否相同的标准为(是否可添加“相同对象”):compareTo()返回0.不再是equals().
      • 定制排序中,比较两个对象是否相同的标准为:compare()返回0.不再是equals().

3、理解无序、可重复

  • 无序
    • 指的是存储在底层数组的数据,并不是按照数组索引的顺序添加的,而是根据数据的哈希值决定的。
    • 无序性,不等于随机性
  • 不可重复
    • 相同的元素只能添加一个。元素按照equals()判断,不能返回true。

4、添加元素的过程(以HashSet为例)

  • 添加过程并不会单独考察,但是对于理解HashMap有很大帮助

  • 我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置),判断数组此位置上是否已经有元素:

    • 如果此位置上没有其他元素,则元素a添加成功。 —>情况1
    • 如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:
      • 如果hash值不相同,则元素a添加成功。—>情况2
      • 如果hash值相同,进而需要调用元素a所在类的equals()方法:
        • equals()返回true,元素a添加失败
        • equals()返回false,则元素a添加成功。—>情况3
  • 对于添加成功的情况2和情况3而言:元素a 与已经存在指定索引位置上数据以链表的方式存储

    • jdk 7 :元素a放到数组中,指向原来的元素。
    • jdk 8 :原来的元素在数组中,指向元素a
    • 总结:七上八下
  • HashSet底层:数组+链表的结构。

5、重写hashCode()和equals()方法

  • 要求:向Set(主要指:HashSet、LinkedHashSet)中添加的数据,其所在的类一定要重写hashCode()和equals()

  • 重写要求:重写的hashCode()和equals()尽可能保持一致性:相等的对象必须具有相等的散列码

  • 重写两个方法的小技巧:对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值。

6、面试题

  • 在List内去除重复数字值,要求尽量简单

    • 可以用Set实现
  •   //程序的输出结果
      HashSet set = new HashSet();
      Person p1 = new Person(1001,"AA");
      Person p2 = new Person(1002,"BB");
      
      set.add(p1); 
      set.add(p2);
      p1.name = "CC";
      set.remove(p1); 
      System.out.println(set);//[Person{id=1002, name='BB'}, Person{id=1001, name='CC'}]
      /*
      存放p1、p2时,根据p1和p2的属性算出了hashCode来确定存放位置
      p1的name改为CC后,再删除p1--->根据此时p1的属性算hashCode来找set中和p1相同的元素,此时算出来的hashCode大概率不是p1的位置,相当于没有找到,所以删除并没有成功。
      */
      
      set.add(new Person(1001,"CC"));
      System.out.println(set); //[Person{id=1002, name='BB'}, Person{id=1001, name='CC'}, Person{id=1001, name='CC'}]
      /*
      存放新建的p,是根据1001和CC来算hashCode,此时数组中这个位置空的,所以存放成功
      */
      set.add(new Person(1001,"AA"));
      System.out.println(set);//[Person{id=1002, name='BB'}, Person{id=1001, name='CC'}, Person{id=1001, name='CC'}, Person{id=1001, name='AA'}]
      /*
      存放新建的p,先hashCode后和p1是一样的,再equals发现不一样,加到链表上,存放成功
      */
    

五、Map接口

1、Map接口概述

  • Map接口继承树

  • Map接口:
    • 存储key-value的键值对,类似于数学中的函数
  • Map实现类对比
Map实现类底层实现线程地位特点
HashMapJDK7及以前:数组+链表
JDK8:数组+链表+红黑树
线程不安全主要实现类可以存储null的k-v
LinkedHashMap同上同上同上可按照添加顺序遍历
TreeMap可排序遍历
Hashtable线程安全古老实现类不可存储null的k-v
Properties常用来处理配置文件

2、面试题

  •     HashMap的底层实现原理★★★★★(见下面第四点)
    
  •     HashMap和Hashtable的异同
    
  •     CurrentHashMap和Hashtable的异同(暂时不学)
    

3、Map结构的理解

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XRFVdCec-1606054949633)(https://img2020.cnblogs.com/blog/2031463/202011/2031463-20201122164554829-917870465.png)]

  • Map中的key:无序的、不可重复的,使用Set存储所有的key —> key所在的类要重写equals()和hashCode() (以HashMap为例);当然如果是LinkedHashMap还要实现排序接口

  • Map中的value:无序的、可重复的,使用Collection存储所有的value —>value所在的类要重写equals()

  • 一个键值对:key-value构成了一个Entry对象。

  • Map中的entry:无序的、不可重复的,使用Set存储所有的entry

四、 HashMap的底层实现原理★★★★★(高频面试题)

1、JDK7

  • HashMap map = new HashMap():底层创建一个长度为16的数组Entry[] table。

  • map.put(key1, value1)

    • 调用key1所在类的hashCode()方法计算key1的哈希值,用哈希值在计算得到在Entry[]数组中的存放位置
      • 如果此位置为空,添加成功------>情况1
      • 如果此位置不为空,比较key1和已经存在的数据的哈希值
        • 如果key1的哈希值和已经存在的数据的哈希值不同,添加成功------>情况2
        • 如果key1的哈希值和已经存在的某一个数据(key2,value2)的哈希值相同,继续调用key1所在类的equals()方法,传入参数key2进行比较
          • 如果equals()返回值为false,添加成功------>情况3
          • 如果equals()返回值为true,用value1替换value2
  • 扩容:(当超出临界值且要添加数据的位置不为空时)默认扩容为原来的2倍,并复制数据到新数组

2、JDK8与JDK7的不同之处

  • HashMap map = new HashMap()时,底层没有直接创建数组,而是在首次put()时创建
  • 数组是Node[]类型,而非Entry[]类型
  • JDK7底层是:数组+链表;而JDK8底层是:数组+链表+红黑树
    • 形成链表时,七上八下(jdk7:新的元素指向旧的元素。jdk8:旧的元素指向新的元素)
    • 当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 > 64时,此时此索引位置上的所数据改为使用红黑树存储。

3、LinkedHashMap底层实现原理

static class Entry<K,V> extends HashMap.Node<K,V> {
    Entry<K,V> before, after;//能够记录添加的元素的先后顺序
    Entry(int hash, K key, V value, Node<K,V> next) {
        super(hash, key, value, next);
    }
}

六、Map接口常用方法

1、添加、删除、修改操作:

  • Object put(Object key,Object value):将指定key-value**添加到(或修改)**当前map对象中

    void putAll(Map m):将m中的所有key-value对存放到当前map中

  • Object remove(Object key):移除指定key的key-value对,并返回value
    void clear():清空当前map中的所有数据

2、 元素查询的操作:

  • Object get(Object key):获取指定key对应的value

  • boolean containsKey(Object key):是否包含指定的key

    boolean containsValue(Object value):是否包含指定的value

  • int size():返回map中key-value对的个数

  • boolean isEmpty():判断当前map是否为空

  • boolean equals(Object obj):判断当前map和参数对象obj是否相等

3、元视图操作的方法:

  • Set keySet():返回所有key构成的Set集合
  • Collection values():返回所有value构成的Collection集合
  • Set entrySet():返回所有key-value对构成的Set集合

4、总结常用方法:

  • 添加:put(Object key,Object value)
  • 删除:remove(Object key)
  • 修改:put(Object key,Object value)
  • 查询:get(Object key)
  • 长度:size()
  • 遍历:keySet() / values() / entrySet()

七、Properties

1、配置文件

  • 默认存放路径为当前工程下

  • 创建配置文件的方式

    • 右键工程名—>new—>File:这时要写上 .properties,如 jdbc.properties
    • 右键工程名—>new—>Resource Bundle:这时秩序写名称就会自动补全后缀
  • 存取数据时,建议使用setProperty(String key,String value)方法和 getProperty(String key)方法

八、Collections工具类

1、Collections概述

  • Collections是一个操作Set、List、Map等集合的工具类
  • Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作, 还提供了对集合对象设置不可变、对集合对象实现同步控制等方法

2、Collections常用方法

排序操作
  • 排序操作都是针对List来讲的,因为Map本身无序,何谈排序
  • 返回值都是void,说明对List本身做了修改
方法解释
reverse(List)反转List中的元素
shuffle(List)随机排序
sort(List)自然排序
Sort(List, Comparator)定制排序
swap(List, int i, int j)交换
查询、替换
方法解释
Object max/min(Collection)返回自然排序的最大、小值
Object max/min(Collection, Comparator)返回定制排序的最大、小值
int frequency(Collection, Object)返回某集合中某元素的出现次数
void copy(List dest, List src)复制
boolean replaceAll(List list, Object oldVal, Object newVal)替换
  • void copy(List dest, List src)中,新List容量不能小于旧List,因此要用 List dest = Arrays.asList(new Object[list.size()]);来创建

3、Collection和Collections的区别(面试题)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Java文艺青年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值