• 什么是集合,有什么用?
    • 数组其实就是一个集合,集合实际上就是一个容器,可以用来容纳其他数据类型
  • 集合为什么说在开发中使用最多?
    • 集合是一个容器,是一个载体,可以一次容纳多个对象,在实际开发中,假设连接数据库,数据库当中有10条数据,那么假设把这10条数据查询处来,在Java程序中会将10条数据封装成10个Java对象,然后将这10个Java对象放到某一个集合当中,将集合传到前端,然后遍历集合,让数据一个个展现出来。
  • 集合不能直接存储基本数据类型,另外集合也不能存储Java对象,集合当中存储的都是Java对象的内存地址(或者说集合当中存储的都是引用)
    • 补:list.add(100);//是因为自动装箱
  • 在Java中每一个不同的集合,底层会对应不同的数据结构,往不同的集合中存储元素,等于将数据放到了不同的数据结构中,什么是数据结构?数据存储的结构就是数据结构,不同的数据结构,存储方式不同,例如:数组、链表、二叉树、哈希表都是不同的数据结构。(在Java中已经写好了这些常用的集合类,不必精通数据结构)
    • new ArrayList();创建一个集合对象,底层是数组
    • new LinkedList();创建一个集合对象,底层是链表
    • new TreeSet();创建一个集合对象,底层是二叉树
  • 集合在Java JDK中哪个包下?java.util.*;所有的集合类和集合接口都在java.util包下
  • 在java中集合分为两大类:
    • 一类是单个方式存储元素,这类集合中超级父接口是:java.util.Collection
    • 一类是以键值对的方式存储元素,这类集合中超级父接口是:java.util.Map
  • 集合的继承结构

 

  • 总结(所有的实现类)
    • ArrayList:底层是数组
    • LinkedList:底层是双向链表
    • Vector:底层是数组,线程安全
    • HashSet:底层是HashMap,放到HashSet集合中的元素等同于放到HashMap集合key部分了
    • TreeSet:底层是TreeMap,放到TreeSet集合中的元素等同于放到TreeMap集合key部分了
    • HashMap:底层是哈希表的数据结构
    • Hashtable:底层是哈希表的数据结构,只不过是线程安全的,效率较低,使用较少
    • Properties:是线程安全的,并且key和value只能存储字符串
    • TreeMap:底层是二叉树数据结构,TreeMap的key底层可以直接按照大小顺序排序、
  • List集合存储元素的特点:
    • 有序、可重复
    • 有序:存进去顺序和取出的顺序相同,每一个元素都有下标
    • 可重复:存进去1,可以再存储一个1
  • Set(对应Map)集合存储元素的特点
    • 无序、不可重复
    • 无序:存进去顺序和取出的顺序不一定相同,另外Set集合中的元素没有下标
    • 不可重复:存进去1,不能再存储1了
  • SortedSet(对应SortedMap)集合存储元素的特点:
    • 无序、不可重复,但是SortedSet集合中的元素是可排序的
    • 无序:存进去顺序和取出的顺序不一定相同,另外SortedSet集合中的元素没有下标
    • 不可重复:存进去1,不能再存储1了
    • 可排序:可以按照大小顺序排序
  •  Map集合的key,就是一个Set集合:在Set集合中放数据,实际放到了Map集合的key部分
  • Collection能存放什么元素:
    • 没有使用泛型之前,Collection可以存放所有Object 的子类型
    • 使用了泛型之后,Collection中只能存放某个具体的类型
    • 集合中不能直接存储基本数据类型,也不能存储Java对象,只能存储Java对象的内存地址
  • Collection中的常用方法
    • boolean add(Object e);向集合中添加元素
    • int size();获取集合中元素的个数
    • void clear() 清空集合
    • boolean contains(Object o)判断当前集合中是否包含元素o,不包含返回false
      • contains方法是用来判断集合中是否包含了某个元素的方法,那么底层是如何判断集合中是否包含了这个元素呢?
        • 调用了equals方法进行对比,equal方法返回true,就表示包含这个元素
    • boolean remove(Object o)删除集合中的某个元素
      • 调用了equals方法进行对比,equal方法返回true,就表示包含这个元素
    • boolean isEmpty()判断集合是否为空
    • Object[] toArray()调用这个方法可以把集合转换成数组
  • 对Collection集合进行遍历:(Collection c=new ArrayList();c.add("abd");c.add("def");c.add(100);c.add(new Object());)
    • 第一步:获取集合对象的迭代器对象Iterator
      • Iterator it=c.iterator();
    • 第二步:通过以上获得的迭代器对象开始迭代/遍历
      • 以下两个方法是迭代器对象Iterator中的方法:
        • boolean hasNext();如果仍有元素可以迭代,则返回true
        • Object next() 返回迭代器的下一个元素

 

    • 一定要注意:集合结构只要发生改变,迭代器必须重新获取
    • 当集合结构发生了改变,迭代器没有重新获取时,调用next()方法时,会出现java.util.ConcurrentModificationException
  • 集合元素的remove(add)
    • 重点:当集合结构发生改变时,迭代器必须重新获取,如果还是以前老的迭代器,会出现异常java.util.ConcurrentModificationException
    • 在迭代集合元素的过程中,不能调用集合对象的remove方法,删除元素:c.remove(o);迭代过程中不能这样,会出现异常java.util.ConcurrentModificationException,但是使用迭代器来删除是可以的
    • 出异常的根本原因是:集合中的元素删除了,但是没有更新迭代器(迭代器不知道集合变化了)
  • List集合存储元素的特点:
    • 有序、可重复
    • 有序:List集合中的元素有下标。从0开始,以1递增
    • 可重复:存储一个1,还可以再存一个1
  • List集合既然是Collection接口的子接口,那么肯定List集合有自己特有的方法(以下列出常用方法)
    • void add(int index,E element) 在列表的指定位置插入指定元素
    • E get(int index)返回列表中指定位置的元素
    • int indexOf(Object) 返回列表中第一次出现的指定位置的索引,如果列表中不包含该元素,则返回-1
    • int lastIndexOf(Object o) 返回列表中最后出现的指定位置的索引,如果列表中不包含该元素,则返回-1
    • E set(int index,E element)用指定元素替换列表中指定位置的元素
    • Object remove(int index,Object element)删除指定下标的元素
  • 计算机英语要知道的几个单词
    • 增:add、save、new
    • 删:delete、drop、remove
    • 改:update、set、modify
    • 查:find、get、query、select
  • ArrayList集合初始化容量及扩容(ArrayList集合是非线程安全的)
    • ArrayList集合默认初始化容量是10(底层创建了一个长度为0的数组,当添加第一个元素的时候,初始化为10)
    • ArrayList集合底层是Object类型的数组Object[]
    • List list1=new ArrayList();   默认初始化容量是10   list1.size();  值为0
    • List list2=new ArrayList(20);   指定初始化容量是20 list2.size();  值为0
    • 集合的size()方法获取的是当前集合中元素的个数,不是获取集合容量
    • ArrayList的扩容,是原容量的1.5倍
      • ArrayList集合底层是数组,怎么优化?
        • 尽可能少的扩容,因为数组扩容效率比较低,建议使用ArrayList集合的时候,预估计元素的个数,给定初始化容量
      • 数组优点:检索效率较高(每个元素占用空间大小相同,内存地址是连续的,知道首元素的地址,然后知道下标,通过数学表达式计算出元素的内存地址,所以检索效率最高)
      • 数组缺点:随机增删效率比较低,另外数组无法存储大数据量(很难找到一块非常巨大的连续的内存空间)
      • 向数组末尾增删元素,效率很高,不受影响
  • 链表
    • 链表的优点:由于链表上的元素空间存储的内存地址不连续,随机增删效率较高(因为增删元素不涉及到大量的元素位移)
    • 链表的缺点:查询效率低,每一次查找某个元素的时候,都需要从头节点开始往下遍历
    • 对于链表数据结构来说,任何一个节点都有两个属性:
      •  第一:存储的数据 
      •  第二:下一个节点的内存地址
    • ArrayLIst是把检索发挥到极致,LInkedList是把随机增删发挥到极致,加元素都是往末尾添加,所以ArrayList比LinkedList使用多
    • LinkedList集合没有初始化容量,最初这个链表没有任何元素,first和last都是null
  • Vector
    • 底层也是一个数组,初始化容量是10,扩容之后的容量是原容量的2倍,ArrayList集合的特点是:扩容之后的容量是原容量的1.5倍
    • Vertor的所有方法都是线程同步的,都带有synchronized关键字,是线程安全的,效率比较低,使用较少
    • 将一个线程不安全的ArrayList集合转换成线程安全的,使用集合工具类java.util.Collections; 
      • java.util.Collection是集合接口
      • java.util.Collections是集合的工具类  Collections.synchronizedList(mylist);
  • 泛型<JDK5.0之后推出的新特性>
    • 泛型这种语法机制,只在程序编译阶段起作用,只是给编译器参考的(运行阶段反省没用)
    • List<Animal> myList=new ArrayList<Animal>();
    • 使用泛型的好处是什么:
      • 第一:集合中存储的元素类型统一了
      • 第二:从集合中取出的元素类型是泛型指定的元素类型,不需要进行大量的向下转型
    • 泛型的缺点是什么:
      • 导致集合中存储的元素缺乏多样性
    • 大多数业务中,集合中元素的类型还是统一的,所以这种泛型特性被大家所认可
    • JDK1.8之后,引入了自动类型推断机制(又称为钻石表达式)
    • List<Animal> myList=new ArrayList<>();
    • ArrayList<这里的类型会自动推断>()
    • 泛型可以自定义
      public class CollectionTest09<eeeeeeeeeeeeeeee> {
          public static void main(String[] args) {
              CollectionTest09<Integer> interger=new CollectionTest09<>();
              interger.method1(123);
          }
          public  void method1(eeeeeeeeeeeeeeee o){
              System.out.println(o);
          }
      }

      自定义泛型的时候,<>尖括号中的是一个标识符,可以随便写,如果不用泛型就是Object类型

    • Java中源代码中经常出现的是:<E><T>:E是Element单词首字母、T是Type类型的首字母
  • JDK5.0新特性:foreach(也叫增强的for循环)
    • for(元素类型  变量名:数组或集合){
      • System.out.println(变量名);
    • }
    • foreach有一个缺点是没有下标,在需要使用下标的循环中,不建议使用增强的for循环
  • java.util.Map接口中常用的方法:
    • Map和Collection没有继承关系
    • Map集合以key和value的方式存储数据,键值对
    • key和value都是引用数据类型
    • key和value都是存储对象的内存地址
    • key起到主导的地位,value是key的附属品
    • void clear()从此映射中移除所有的映射关系
    • boolean containsKey(Object key)如果此映射包含指定键的映射关系,则返回true,底层调用equals方法,就算new新对象也可以。
    • boolean conrainsValue(Object value)如果此映射将一个或多个键映射到指定值,则返回true,则返回true,底层调用equals方法,就算new新对象也可以。
    • V get(Object key)返回指定键所映射的值
    • boolean isEmpty()如果此映射未包含键值关系,则返回true
    • V put(K key,V value)将指定的值与此映射中指定的键关联
    • V remove(Object key)如果存在一个键的映射关系,则将其从映射中移除
    • int size()返回此映射键—值映射关系数
    • Set<Map.Entry<K,V>> entrySet()返回此映射中包含映射关系的Set视图,Map.Entry属于静态内部类  1=zhangsan
    • Set<K> keySet();获取Map集合的所有的key(所有的键是一个set集合)
  • 哈希表的数据结构(数组和单向链表的结合体)
    • 数组:在查询方面效率高,随机增删方面效率很低
    • 单线链表:在查询方面效率低,随机增删方面效率很高
    • 哈希表将以上两种数据结构融合在一起,充分发挥他们各自的优点
    • 哈希表相当于是一个数组,数组中每个元素都可以是一个单链表,单链表中存储数据元素
    • 哈希值是key的hashCode()方法执行的结果,hash值通过哈希函数/算法,可以转换成数组的下标
    • e为什么哈希表的随机增删,以及查询效率都很高?

      • 增删是在链表上完成的
      • 查询也不需要都扫描,只需要部分扫描即可
      • 重点:HashMap的key会先后调用两个方法,一个方法是hashCode方法,一个方法是equals方法,那么这两个方法都需要重写
    • HasMap集合key部分的特点:
      • 无序、不可重复
      • 无序:挂上去的顺序和取下的顺序不一样
      • 不可重复:equals方法来保证HashMap集合的key不可重复,如果key重复了,value会覆盖
    • 放在HashMap集合的key部分的元素其实就是放到了HashSet集合中了,所以HashSet集合当中的元素也需要同时重写hashCode()+equals()方法
    • 哈希表HashMap使用不当无法发挥性能!
    • 假设将所有的hashCode()方法返回固定的某个值,那么会导致底层的哈希表变成纯单向链表,这种情况下我们称为:散列分布不均匀
    • 什么是散列分布均匀?
      • 假设100个元素,10个单向链表,那么每个单向链表上有10个节点,这是最好的,是散列分布均匀的  
    • 假设将所有的hashCode()方法返回值都设定为不一样的值,可以吗,有什么问题?
      • 不行,因为这样的话导致底层的哈希表就成为一维数组了,没有链表的概念了,也是散列分布不均匀
    • 散列分布均匀需要重写hashCode方法时,有一定的技巧
    • HashMap集合的默认初始化容量是16,默认加载因子是0.75,这个默认加载因子是当HashMap集合底层数组的容量达到75%的时候,数组开始扩容。在JDK1.8之后,如果哈希表单项链表中元素超过8个,单向链表这种数据结构会变成红黑树数据结构,当红黑树上的节点数小于6时,会重新把红黑树变成单向链表数据结构,这种方式是为了提高检索效率,二叉树的检索会再次缩小扫描范围,提高效率。
    • 重点:记住HashMap集合初始化容量必须是2的倍数,也是官方推荐的,这是因为达到散列均匀,为了提高HashMap集合的存取效率,所必须的。
    • 因为两个对象相等,哈希值一定相等,所以equals方法重写时,hashCode方法也必须重写,并且equals方法返回如果是true,hashCode方法返回值必须一样
    •  以下是关于hashcode的一些规定:
      • 两个对象相等,hashcode一定相等
      • 两个对象不等,hashcode不一定不等
      • hashcode相等,两个对象不一定相等
      • hashcode不等,两个对象一定不等
    • 对于哈希表数据结构来说:
      • 如果o1和o2的hash值相同,一定放到同一个单链表上,
      • 当然如果o1和o1的hash值不同,但由于哈希算法执行结束之后转换的数组下标可能相同,此时会发生“哈希碰撞”
    • HashMap和Hashtable的区别:
      • HashMap的key和value都可以为null,默认初始化容量是16,默认加载因子是0.75,扩容之后的容量时原容量的2倍,,HashSet的容量和扩容也一样
      • Hashtable的key和value都不能为null,默认初始化容量是11,默认加载因子是0.75,扩容之后的容量时原容量的2倍+1
    • TreeSet和TreeMap:
    • TreeSet集合底层实际上是一个TreeMap
    • TreeMap底层是一个二叉树
    • 放到TreeSet集合中的元素,等同于放到了TreeMap集合的key部分
    • TreeSet集合中的元素,无序、不可重复,但是可以按照元素的大小顺序排序,称为可排序集合