集合概述
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<>();
集合类整体总结
是否记录添加顺序的总结如下:
数据类型 | 是否记录添加顺序 | 是否添加就排序 | 底层算法 |
---|---|---|---|
arrayList | true | false | 数组 |
linkedList | true | false | 链表 |
hashSet | false | false | 哈希算法,红黑树 |
linkedHashSet | true | fasle | 哈希算法,链表,红黑树 |
treeSet | false | true | 红黑树 |
hashMap | false | false | 哈希算法,红黑树 |
linkedHashMap | true | false | 哈希算法,链表,红黑树 |
treeMap | false | true | 红黑树 |