目录
5.1.1.了解Hashtable,LinkedHashMap,TreeMap
1.数组和集合
数组
数组和集合都是对多个数据进行内存存储操作,不是持久化存储,简称容器。
数组的特点
- 一旦指定长度,就不可以更改
- 申明了同一类型数据就只能存放该类型的数据
数组的缺点
- 数组长度确定了就不可更改
- 删除,增加元素,都效率低下
- 数组中实际元素的数量没有提供对应的方法或者属性来获取
- 数组存储:有序,可重复,对无序,不可重复的数组不能满足要求
针对上诉缺点,就引入了新的存储数据结构-----集合
不同的集合数据结构不一样,集合不一样,特点也不一样
集合
集合分两类
- 一个数据一个数据的存储(Collection)
- 一对一对的存储(Map)
2.Collection的常用方法
增加:boolean add(E e);boolean addAll(Collection<? extends E> c)
删除:void clear();boolean remove(Object o)
查看:Iterator<E> iterator();int size()
判断:boolean contains(Object o);boolean equals(Object o);boolean isEmpty()
循环方式只有两种:
- 增强for循环
- iterator迭代器
Collection<Object> collection = new ArrayList<>();
collection.add("1");
//增强for循环
for(Object obj : collection){
System.out.println(obj);
}
//迭代器
Iterator<Object> iterator = collection.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
3.List集合
不唯一,有序
常用的实现类有ArrayList,Vector,LinkedList等
常用方法
增加:add(int index,E element);
删除:remove(Object o);remove(int index)
修改:set(int index,E element)
查看:get(int index)
遍历方式:
- 普通for循环
- 增强for循环
- iterator迭代器
扩展的方法都与index相关
3.1.ArrayList
底层跟stringbuilder类似,两个重要的属性:
Object[] elementData;//Object类型的数组,默认长度10
int size;//当前数组已经使用的大小,默认0
3.1.1.jdk1.7
在初始化时候,直接初始化数组elementData,默认数组长度是10
public ArrayList(){
this(10);
}
ArrayList在Add()方法中,
- 触发条件:原来的数组都满了之后才扩容
- 扩容原有长度的1.5倍:int newCapacity = oldCapacity + (oldCapacity >> 1);
- 将老数组中的内容复制到新的数组中,然后返回新数组
例如:原来的数组长度是10,扩容后新的数组长度是15。
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
3.1.1.jdk1.8
在初始化时候,直接初始化数组elementData,默认数组长度是0
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
ArrayList在Add()方法中,底层才创建默认长度为10的数组,比jdk1.7节省了内存,剩下的都跟jdk1.7一样
ArrayList在jdk1.7和1.8的比较
默认构造器:
- 1.7默认初始化长度是10的数组
- 1.8默认初始化长度是0的数组
add()方法:
- 1.7数组满了之后开始数组扩容
- 1.8默认初始化长度是10的数组,如果数组满了就开始数组扩容
3.2.Vector(已废弃)
跟ArrayList高度类似,两个重要属性:
Object[] elementData;
int elementCount;
默认构造器,elementData数组长度10
public Vector() {
this(10);
}
所有方法都加了线程安全的关键字synchronized
数组扩容是原有长度的2倍:int newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);
}
区别和优缺点
底层都是数组的扩容
与ArrayList的区别
- ArrayList底层扩容长度是数组的1.5倍,线程不安全,但效率高
- Vector底层扩容长度是数组的2倍,线程安全,但效率低,所有被淘汰使用
都具备数组的优缺点
- 查询效率高
- 删除,增加元素,效率低
迭代器, Iterable接口,iterator()方法,Iterator接口 ,三者的区别?
ArrayList类中的内部类Iter实现了Iterator接口,有三个重要的方法:
- boolean hasNext() //是否是最有一个元素
- E next() //获取当前游标cursor的数组元素
- remove() //删除当前游标cursor的数组元素
ArrayList类中的内部类ListItr实现了ListIterator接口,是Iter的增强版,可以解决并发修改异常(ConcurrentModificationException)。
List<Integer> lists = new ArrayList<>();
ListIterator<Integer> listIter = lists.listIterator();
while(listIter.hasNext()){
Integer i = listIter.next();
if(i == 3){
listIter.add(2);
}
}
3.3.LinkedList
常用方法
增加:addFirst(E e);addLast(E e);
offer(E e);offerFirst(E e);offerLast(E e)
删除:poll();
pollFirst();pollLast();
removeFirst();removeLast();//1.6新提出的方法,不抛异常,增强了健壮性
查看:element()
getFirst();getLast();
indexOf(Object o);LastIndexOf(Object o)
peek();
peekFirst();peekLast();
底层原理
逻辑结构下
ArrayList:线性表(数组);LinkedList:线性表(双向链表)
双向链表中每一个节点包含:
- 前一个元素的地址;
- 当前元素值;
- 后一个元素的地址
jdk1.7和jdk1.8源码都一样,证明如下:
transient int size = 0;//链表的使用大小
transient Node<E> first;//上一个节点对象
transient Node<E> last;//下一个节点对象
//内部类
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
4.Set集合
唯一,无序
常用的实现类:HashSet,LinkedHashSet,TreeSet等
4.1.HashSet
底层:哈希表(HashMap或者数组+链表)
除了系统提供的引入数据类型(Integer,String等)之外,自定义引入数据类型(User,Dog等)一定要重写equals和hashCode方法,才能保证无序和唯一。
两个关键属性
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
原理(以Integer为例)
底层是HashMap,HashMap的底层是哈希表(数组+链表),具体看HashMap的底层原理
public HashSet() {
map = new HashMap<>();
}
在添加数据的时候,
- 调用对应的hashCode方法计算哈希值
- 通过哈希值和一个表达式计算在数组中的存放位置
- 如果发生哈希碰撞,需要key的hashcode,equals比较和==比较
4.2.LinkedHashSet
特点:唯一,有序(按照元素的存放顺序)
底层:哈希表
继承了HashSet,在HashSet的基础上多了一个总的链表,这个链表将放入的元素串在一起,方便有序的遍历
4.3.TreeSet
特点:唯一,有序(默认按照升序,二叉树的中序遍历)
底层:二叉树(TreeMap)
二叉树的遍历
- 先序遍历:根 左 右
- 中序遍历:左 根 右 ==>当前TreeSet的遍历方式
- 后序遍历:左 右 根
如何做到的有序呢?
利用了接口中的内部比较器或者外部比较器
4.3.1.内部比较器
implements Comparable<>
@Override
public int compareTo(Test o){}
例如:Integer实现了内部比较器
implements Comparable<Integer>
public int compareTo(Integer anotherInteger) {
return compare(this.value, anotherInteger.value);
}
例如:String实现了内部比较器
implements Comparable<String>
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
自定义类,也需要实现内部比较器,重写compareTo()方法
4.3.2.外部比较器
implements Comparator<>
@Override
public int compare(Test o1, Test o2) {
return 0;
}
使用方式如下:
Comparator comparator = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return 0;
}
};
TreeSet<String> treeSet = new TreeSet<String>(comparator);
//或者jdk8写法:
TreeSet<String> treeSet = new TreeSet<String>((o1, o2) -> {
return 0;
});
5.Map集合
特点:Key,Value键值对方式存储,Key不允许重复,Value可以重复
常用的实现类:HashMap,HashTable,TreeMap等
常用方法
增加:put(K key,V value)
删除:clear();remove(Object key)
查看:entrySet();get(Object key);keySet();size();value();
判断:containsKey(Object key);containsValue(Object value);
equals(Object o);isEmpty();
5.1.HashMap
特点:key唯一,无序
底层:哈希表(数组+链表)
哈希表原理:数据对应的类型,必须重写hashCode方法和equals方法
5.1.1.了解Hashtable,LinkedHashMap,TreeMap
Hashtable(已废弃)与HashMap,与ArrayList和Vector类似
HashMap:JDK1.2,效率高,线程不安全,key可以存入null值,并且key的null值也是遵循唯一的特点
Hashtable:JDK1.0,效率低(方法都加入了Synchronized关键字),线程安全,key不可以存入null值
LinkedHashMap继承了HashMap,与LinkedHashSet类似
特点:唯一,有序(按照输入顺序进行排序)
底层:哈希表
TreeMap,与TreeSet类似
特点:唯一,有序(按照升序或者降序)
原理:二叉树,key遵循二叉树的特点,放入集合的key对应的数据类型一定要实现比较器(内部比较器,外部比较器二选一)