Java 复习笔记7 - 集合框架

集合从概念上来看就是一堆元素放在一块儿,咱们其实可使用数组来处理这样的状况(操做一堆数据),可是数组存在各类各样的限制,使用起来并不方便,所以才推出更强大的集合处理方式,即集合框架html

数组的问题:java

image-20191126160655434

容器最基本的功能就是存储数据,取出数据,可是由于实际需求不一样,好比是否有序,是否可重复,不一样的数据结构,不一样的存取效率等等缘由,系统提供了一众不一样特性的容器类,共同构成了集合框架算法

框架体系结构:api

image-20191125161828534

其中Collection接口支持(单个形式)数据的存储,而Map接口支持对键值对形式的数据的存储数组

List接口

特色:

  • 有序存储
  • 可重复存储
  • 可存储null
  • 提供按照索引的操做方法
  • 动态增加(chang)

主要实现类:

  • ArrayList
    • 对于列表尾部的插入删除效率更高
    • 随机读取快,查找快,随机插入删除慢
    • 底层使用数组实现(连续的内存)

      ps: 因为其内部使用数组,而数组容量是固定的,这就致使了,添加删除时都会引起数组的重建,以及数据的拷贝,数据结构

      举个例子:火焰山终于下雨了,刚开始觉得只下一点点,因此找了一个杯子装,后来发现装不下了,要换一个盆(建立更大的数组),那么须要先把杯子里的水倒进盆里(数据拷贝)并发

      适用于:读取,查询操做更频繁的场景框架

  • LinkedList
    • 内部使用双链表结构(非连续的内存)
    • 随机添加删除和顺序添加删除都很快
    • 查找慢,随机读取慢

      ps:因为使用的链表结构,因此添加删除时仅须要在任意内存保存数据而后将指针添加到上一个数据的结尾便可,因此这添加删除数据时比使用数组的ArrayList快不少 ,可是当须要查找数据时,就须要频繁的移动指针(地址不是连续的),形成了查询速度慢,ide

      举个例子: 你是要找王老板作生意,可是你本身不认识他,因而你就找了刘老板,刘老板也不认识他,因而刘老板找了李老板,还好最后李老板认识王老板,找到了你要找的人,期间不能跨越中间的必须一个一个找下去学习

      适用于:添加,删除操做更频繁的建立

Set接口

主要实现类:

  • HashSet
    • 不保证迭代顺序
    • 不可存储重复元素
    • 能够存储null元素
    • 内部使用HashMap(具体参考下面HashMap内容),将全部键值对的值设置为同一个Object对象

能够说HashSet就是对HashMap进行了简单的包装:

​ 真正存储数据的容器:

image-20191126141916782

​ 包装后的添加数据的方法:

image-20191126141859533

iterable接口

iterable叫作可迭代对象

咱们知道集合框架提供了不少不一样的容器实现类,可是每种容器的内部实现不一样,致使咱们须要记忆不一样的取值方法,这对于使用者来讲无疑增长学习成本,因此Java统一了各类容器的取值方式,即经过iterable接口

好处:
  • 实现了iterable的类均可以被foreach循环直接使用
接口方法:
public Iterator iterator();//用于返回一个迭代器接口对象

Iterator接口

Iterator叫作迭代器

迭代器用于获取容器中的值

接口方法:
public boolean hasNext(); //判断是否还有下一个
public Integer next();//获取下一个的值

使用案例:

//写一个支持foreach语法的类
import java.util.Iterator;
public class MyTest implements Iterable<Integer> {
    private int num = 0;
  
    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            @Override
            public boolean hasNext() {
                return num < 11;
            }
          
            @Override
            public Integer next() {
                return num++;
            }
        };
    }

    //测试:
    public static void main(String[] args) {
        for (Integer i:new MyTest()) {
            System.out.println(i);
        }
    }
}
//该类能够迭代获得0-10这11个数字

ps:collection接口继承了iterable,因此全部collection的子类能够被foreach遍历

集合的遍历:

咱们可使用foreach来遍历collection的任何子类对象:

案例:

HashSet<String> set = new HashSet<String>();
set.add("1");
set.add("2");
set.add("3");

ArrayList<String> list = new ArrayList<String>();
list.add("1");
list.add("2");
list.add("3");

for (String s: list) {
    System.out.println(s);
}

for (String s: set) {
    System.out.println(s);
}

须要特别强调的是不容许在迭代期间删除或添加元素,将会引起:java.util.ConcurrentModificationException异常,意思是不容许并发修改

那么咱们如何删除元素呢?

迭代期间添加删除元素

//删除或添加单个元素:
ArrayList<String> list = new ArrayList<String>();
        list.add("1");
        list.add("2");
        list.add("3");
for (String s: list) {
  list.add("11111");
  break;//操做完成后直接break;
}
//删除或添加多个元素方法1
//先保存须要删除的元素,迭代完成在一并删除
ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(1);
        list.add(2);
        list.add(3);

ArrayList deletes = new ArrayList();
for (Integer i:
     list) {
  if (i > 1){
    deletes.add(i);
  }
}
list.removeAll(deletes);


//删除或添加多个元素方法2
//使用lambda表达式  jdk1.8以后可用
list.removeIf(i -> i > 1);

//删除或添加多个元素方法3
//迭代copy的集合,从原始集合删除
for (Integer i:
     (ArrayList<Integer>)list.clone()) {
  if(i > 1){
    list.remove(i);
  }
}

//删除或添加多个元素方法4
//使用迭代器
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()){
  if (iterator.next() > 1){
    iterator.remove();
  }
}

Map接口

特色:

  • 数据已键值对的形式存储
  • 键值对用Entry类对象表示
  • 不能包含重复的key
  • 键值对一一对应

注意:

  • 避免使用可变对象做为key,若是对象的值改变时会引发equals方法的变化,将会致使不可预知的问题,,简单的说,map之因此不存储重复的key是由于,map使用hashCode+equals方法判断是否重复,若是你使用的是可变对象,那么当对象的属性变化时,可能致使判断重复的机制发生错误,例如:原本插入了Person对象,名字为张三和李四,后来你把张三的名字改为李四,那么这时候就误判断这两个对象key是否相同
  • 如何判断对象是否相等
    • 第一步调用对象的hashCode得到一个整数进行比较,若是相同再调用equals方法进行判断,若是equals也返回true的话则表示两个对象彻底相同,这样的对象是不会被存储的
  • 综上所述,咱们在自定义类中若是进行去重,则应当重写这两个方法(hashCode,equals)

主要实现类:

  • HashMap
    • 基于Hash表实现
    • 容许使用null做为key或value
    • Map中的Entry(键值对)是无序的
    使用方法

遍历方法:

//HashMap,不是Collection的子类因此不能直接foreach
//可用使用下面几种方式:
//方法1
for ( String key: map.keySet()) {
  System.out.println(key+" "+map.get(key));
}
//方法2  
for (Map.Entry entry:map.entrySet()){
  System.out.println(entry.getKey() +" "+entry.getValue());
}

HashMap 数据结构解析:

  • HashMap内部使用hash表(本质是一个数组见图一)

  • HashMap使用hash算法计算获得存放的索引位置,一次来加快查询速度,(比ArrayList还要快)

  • 一样的既然本质是数组则少不了扩容和复制数据的问题了,这与ArrayList的缺点是同样的
  • hash值相同不能表示key彻底相同,须要调用equals再次确认

  • 若是说key的hash值相同可是equals判断不相同,那么使用树结构或者链表来存储这些hash相同的元素,(具体使用哪一种根据当前map中元素的数量,超过64个元素则使用树结构)

image-20191126123312490

获取元素方法:

image-20191126123117994

存储元素方法:

image-20191126134535014

存储元素方法补充:

代码在putVal方法的最后,当添加元素后的大小超过阈值时则直接扩容hash表

image-20191126135920717

扩容/转换方法:

image-20191126130936107

总结:

  • HashMap是使用Hash表(本质是数组)来存储数据,

  • 当keyHash值相同可是equals判断不一样时使用链表(添加快,查询慢)

  • 当元素数量超过64时则将链表转为数结构(添加慢,查询快)

最后再来一张框架图,帮助理解:

image-20191126160453485

图中的Listiterator 能够实现倒叙迭代,须要的话咨询查询API

相关文章
相关标签/搜索