Java之集合

集合概述

1. 集合框架(java.util)的由来:容器类(集合类)可以存储多个数据,数组明明可以存储多个数据,为啥还要定义容器类?

  • 数组弊端:

    • 长度一但固定就不可变

    • 很多地方需要操作数组的(增删改查)都需要去编写对应的方法(代码重复了--->封装)

    • 每个人定义各自的方法,可能存在别人找不到 这种情况,实现也容易存在bug

2. 什么是集合框架:容器类确实很好用,集合框架框架是为了提供一些规范和标准,任何实现类都需要包含对外的接口,接口的实现,对集合内部的算法(底层都是一种数据结构)

3. 定义集合框架的目的:提供代码复用(封装的思想),让使用者专注于业务开发,而不是数据结构和算法。

4. 常见集合类:

  • List(列表):集合中对象按照索引位置排序,允许元素重复。

  • Set(集):集合中的元素不按特定方式排序,不允许元素重复。

  • Map(映射):集合中的元素(key-value),不允许key 重复,值可以重复。

 

Collection 常用方法

1. 接口定义的常用方法规范

//集合容量大小 
int size();
//判断集合是否为空
boolean isEmpty();
//包含是否有某一个元素
boolean contains(Object o);
//迭代器
Iterator<E> iterator();
//转换成数组
Object[] toArray();
//添加元素的
boolean add(E e);
//删除元素
boolean remove(Object o);
//判断集合是否包含另一个集合
boolean containsAll(Collection<?> c);
//添加一个集合
boolean addAll(Collection<? extends E> c);
//清空
void clear();
//获取hashcode
int hashCode();

2. 通用迭代  

Iterator it = 集合对象.iterator();
while(it.hasNext()){
    Object ele = it.next();
}

//迭代器接口
//判断是否有下一个元素
boolean hasNext();
//获取下一个元素
E next();

List 接口

1. List 接口规范:

//根据索引获取元素值 
E get(int index);
//设置某一个索引位置元素值
E set(int index, E element);
//删除某一个索引位置的值
 E remove(int index);
//截取
List<E> subList(int fromIndex, int toIndex);

Vector 实现类

1. 看源码学习

public class Vector<E> extends AbstractList<E> implements List<E>{
       //元素数组
      protected Object[] elementData;
      //元素个数
      protected int elementCount;
      //扩容容量
      protected int capacityIncrement;
    
    public Vector(int initialCapacity, int capacityIncrement) {
        this.elementData = new Object[initialCapacity];
        this.capacityIncrement = capacityIncrement;
    }
    
     public Vector(int initialCapacity) {
        this(initialCapacity, 0);
     }
    
    public Vector() {
        this(10);
    }
    
     public synchronized void addElement(E obj) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = obj;
    }
    //扩容逻辑
     private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
         //如果你传进来扩容容量,新容量=老容量+传进来的扩容容量否则2倍老容量
        int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                         capacityIncrement : oldCapacity);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            //给int类型的最大值
            newCapacity = hugeCapacity(minCapacity);
         //数组的拷贝
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    
    
}

2. Vector 总结  

    • 底层使用 Object[] 数组(调用不带参数构造器时,默认长度为10,若不传扩容参数,扩容2倍)

    • toString 方法已经重写并且可以直接打印出数组的样子

    • 增查删改

    • 常用方法

      • add(E obj)

      • addElement(E obj)

    • 查询

      • size() 查长度

      • get(int index) 查具体索引位置的元素值

      • isEmpty() 判断集合为空

    • 删除

      • remove(int index)删除具体索引位置的元素

      • remove("A") 删除指定元素

      • removeLast() 循环,设置 null ,等待gc 回收

    • 修改

      • set(int index,E obj);修改某一个索引位置元素值

Stack 栈

栈:是数据结构,First In Last Out

栈结构生活中的体现:

  • QQ消息:AB两个人先后发送消息,最后发送的是最新的。

栈:底层可以用数组,或者链表

//添加元素
public E push(E item) {
        addElement(item);
        return item;
}
//取元素并且删除
 public synchronized E pop() {
    E       obj;
    int     len = size();
    obj = peek();
    removeElementAt(len - 1);
    return obj;
}

//查看栈顶元素
public synchronized E peek() {
    int     len = size();
    if (len == 0)
        throw new EmptyStackException();
    return elementAt(len - 1);
}
  • 建议:建议使用ArrayDeque(方法会更加友好)

ArrayList

ArrayList 是用来取代 Vector.两者底层原理和算法,一模一样。

区别:

  • Vector:所有的方法都是用 synchronized 修饰符,表示线程安全的,性能低,适用于多线程环境

  • ArrayList:线程不安全,性能高,即使在多线程环境下也是用它(Collections.synchronizedList(list))

  • ArrayList 底层扩容是1.5倍,Vector 是两倍

  • 底层构造器ArrayList 优化了,默认创建对象的时候是给一个空数组,第一次调用add 方法时,采取重新初始化数组(创建对象时,如果不存任何值,也浪费了堆空间)

LinkedList

1. LinkedList:是一个双向链表,双向队列(单向队列)list 的实现类。  

Queue 方法DQueue 方法LinkedList
boolean add(E e);void addFirst(E e);public E getFirst()
E remove();void addLast(E e);public E getLast()
E poll();E removeFirst();public void addFirst(E e)
E element();E removeLast();public void addLast(E e)
E peek();E pollFirst();
E pollLast();
E getFirst();
boolean add(E e);
void push(E e);
E pop();

2. 面试题:请编写一个双向链表

3. 注意:

  • LinkedList 是非线程安全的,保证线程安全,使用Collections 线程安全方法

  • 擅长操作头和尾,大多数你以后要用的方法, addFirst addLast removeFirst

  • 链表不存在索引,但是调用get(index) 是因为底层提供了 ListItr 这个内部类 提供了一个int 的索引

  • 擅长保存和删除操作

List 的总结

1. 根据 Vector ArrayList LinkedList 类的所有特点进行一个抽象,定义一个规范

2. 特点:

  • 允许元素重复

  • 会记录添加顺序

  • 具有很多共同方法

实现类的选用

1. ArrayList 取代 Vector 直接使用

2. LinkedList:

  • 数组结构的算法:插入和删除速度慢,查询和更改较快

  • 链表结构的算法:插入和删除速度快,查询和更改较慢

泛型

1. 为啥使用泛型?

  • 存在一定问题:

    • 取集合元素时,取出来的是Object 类型,需要强制类型转换才能使用

    • 添加元素时候,缺乏规范,导致可能需要使用时,会出现类型转换异常

  • 设计原则,不要写重复的代码,能抽就抽

2. 泛型:是一种数据规范和约束,提供编译时期的安全检查机制,底层给我们做强制类型转换

3. 如何使用泛型?

  • 常见的字母

    • T type(类型,使用到类上面)

    • K V (key value)

    • E element(元素)

  • 使用

    • 使用到类或者接口上

      public class User<T> {
          T obj;
      }
      
      //多个的方法
      public class User<T,E,k> {
          T obj;
          E ele;
          k value;
      }
    • 接口

      public interface Usb<T> {
          void user(T t);
      }
    • 方法

      public static <T> T print(T t){
          return t;
      }
    • 泛型的继承

      public class Mouth<T,T1> implements Usb<T,T1>{
          @Override
          public void user(T t, T1 t1) {
          }
      }
      
      public class Keyword<T> implements Usb<T,String>{
          @Override
          public void user(T t, String s) {
          }
      }

Map 接口

1. map 概述:key--->value 的映射关系,是以键值对的方式存储

2. Map:是一种两个集合之间的一个映射关系,Map 并没有继承 Collection 接口

  • key 是不可以重复

  • value 是可以重复的、

3. Map 最常用的实现类:

  • HashTable

  • HashMap

  • LinkedHashMap

  • Properties

  • ConcurrentHashMap

4. map 常用的方法

int size();
//判断某一个key 是否存在
boolean containsKey(Object key);
//获取元素值
V get(Object key);
//设置值
V put(K key, V value);
//删除某一个key 的值
V remove(Object key);
//获取所有的key
 Set<K> keySet();
//获取所有的value
Collection<V> values();
//获取键值对对象
Set<Map.Entry<K, V>> entrySet();

HashMap

1. put 方法的实现原理  

public V put(K key, V value) {
    return putVal(hash(key), key, value, false, true);
}

hash(key);

Node<K,V> 单向的
    
TreeNode<K,V>

2. get 方法原理

hash(key)//先找hash表中的位置
//判断是treeNode,如果是使用红黑树算法寻找value,不是使用链表遍历
treeNode 算法,使用hash 往左节点或者右节点直接找。
    
遍历链表,判断hash 的同时,去判断 key 值

1. 面试题:java7 个 hava8 hashMap 的区别

2. 使用

  • key --- String

  • value 无所谓

  • 注意,存String 类型无所谓,存对象类型,需要重写equals 方法和 hashCode();

Map<Student,String> mapStu = new HashMap<>();
mapStu.put(new Student(10),"sy");
mapStu.put(new Student(12),"zs");
mapStu.put(new Student(13),"ls");
mapStu.put(new Student(10),"zz");

Set<Map.Entry<Student, String>> entries = mapStu.entrySet();
System.out.println(entries);
for (Map.Entry<Student, String> entry : entries) {
    System.out.println(entry.getKey());
    System.out.println("-----");
    System.out.println(entry.getValue());
}
  • 嵌套使用  

LinkedHashMap

1. 记录添加顺序,key 不允许重复,判断 key 是否重复和 HashMap 标准一致

HashTable

1. HashTable:和HashMap 几乎一致,它是线程安全的,被HashMap 所替代了。采用hash表算法,所有的方法synchronized修饰符修饰,性能低一点。

Properties

用来加载资源文件

public synchronized void load(InputStream inStream) throws IOException {
    load0(new  LineReader(inStream));
}

TreeMap

1. 采用红黑树算法,里面的key 会按照自然顺序(不自己指定排序规则)自动排序,或者定制排序,可以不能重复,key 判断重复的标准,Comparator compare 的返回值判断

  • 返回值 = 0

  • 返回值 > 0 升序 ASC

  • 返回值 < 0 降序 DESC

2. 需求:计算一个字符串中字符出现的次数 "fwihfiwehfihewifhiaufiuhailfawigfuauekgfweufuiwegfieuw",并按照 a b c 的方式排序;

String str = "fwihfiwehfihewifhiaufiuhailfawigfuauekgfweufuiwegfieuw";
char[] chars = str.toCharArray();
Map<Character,Integer> map = new TreeMap<>();
for (char aChar : chars) {
    //如果从map 里面找不到,说明没值
    if(map.get(aChar) == null ){
        map.put(aChar,1);
    }else{
        Integer integer = map.get(aChar);
        map.put(aChar,++integer);
    }
}

System.out.println(map);

3. 排序源码

Set 接口

1. Set 接口是 Collection 的子接口,相当于数学上的集合

2. Set 存储元素的特点:

  • 不允许元素重复,尝试添加相同元素,会返回false

  • 不会记录元素的先后添加顺序

  • 判断两个元素对象是否相等用的是equals 方法

3. hash 表与 数组对比

HashSet

1. HashSet 是 Set 最常用的接口,底层使用 Hash(散列) 算法,查询速度和插入速度较快。

2. HashSet 判断两个对象是否相等,equasl 比较。返回 true 表示相等。

3. 对象的 HashCode 值决定了在hash 表中的位置。

  • 判断添加对象和集合元素对象HashCode值。

    • 不等:直接将新添加对象存储导对应位置

    • 相等:再继续判断新对象和集合中对象的具体值,equals 方法判断

      • HashCode 相同,equals true ,则是同一个对象,则不保存hashtable 中

      • HashCode 相同,equasl false,存储到同槽的链表上。

  4. HashSet 基于 HashMap 实现的

//直接使用HashMap写好的代码,体现了封装的原则,减少重复代码
private transient HashMap<E,Object> map;
//map.put value 永远设置同一块空间 new Object 的静态常量
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}
//得到 HashMap 的 key
public Iterator<E> iterator() {
    return map.keySet().iterator();
}

5.注意:记得重写 equals 和 hashCode 方法

LinkedHashSet

1. 底层使用哈希表算法,和链表算法,

  • 哈希表用来保证唯一性,HashSet 里面是不记录添加先后顺序的。

  • 链表,来记录元素的添加顺序

2. 底层基于LinkedHashMap 实现

public LinkedHashSet() {    
    super(16, .75f, true);
}

​HashSet(int initialCapacity, float loadFactor, boolean dummy) {
    map = new LinkedHashMap<>(initialCapacity, loadFactor);
}

TreeSet

1. TreeSet:底层使用红黑树算法,会对存储的元素做自然排序(小-大)

2. 注意:使用TreeSet 时,必须使用同一种数据类型。因为需要比较,否则就会报类型转换异常

  • 底层肯定实现了Comparable 接口

  • 比较结果

    • 大于0

    • 等于0 则说明是同一个对象

    • 小于0

3. 底层都是基于 TreeMap 实现的

//底层使用 treeMap 
public TreeSet() {
    this(new TreeMap<E,Object>());
}
//可以传一个自定义比较器
public TreeSet(Comparator<? super E> comparator) {
    this(new TreeMap<>(comparator));
}

Collections集合工具类

1. 常用方法

//获取线程安全的集合
 Collections.synchronizedList(new ArrayList());
//获取线程安全的集合
Collections.synchronizedCollection(Collection);
//排序,好挺常用
public static <T extends Comparable<? super T>> void sort(List<T> list) {
    list.sort(null);
}

2. 其它常用

public static final List EMPTY_LIST = new EmptyList<>();
public static final Map EMPTY_MAP = new EmptyMap<>();
public static final Set EMPTY_SET = new EmptySet<>();

集合类整体总结

 是否记录添加顺序的总结如下:

数据类型是否记录添加顺序是否添加就排序底层算法
arrayListtruefalse数组
linkedListtruefalse链表
hashSetfalsefalse哈希算法,红黑树
linkedHashSettruefasle哈希算法,链表,红黑树
treeSetfalsetrue红黑树
hashMapfalsefalse哈希算法,红黑树
linkedHashMaptruefalse哈希算法,链表,红黑树
treeMapfalsetrue红黑树
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值