集合
集合
Collection
List
-
有序,有下标,元素可以重复
实现类
-
ArrayList
数组结构实现,查询快,增删慢(必须开辟连续的空间)
jdk1.2版本,运行效率快,线程不安全
-
Vector
数组结构实现,查询快,增删慢
jdk1.0版本,运行效率慢,线程安全
-
LinkedList
链表结构实现,查询慢,增删快(无需开辟连续的空间)
jdk1.2版本,运行效率快,线程不安全
为什么ArrayList查询效率高,删除效率慢
ArrayLIst查询效率高:ArrayLIst是连续存放元素的,找到第一个元素的首地址,再加上每个元素的占据的字节大小就能定位到对应的元素。
LinkedList插入删除效率高。因为执行插入删除操作时,只需要操作引用即可,元素不需要移动元素,他们分布在内存的不同地方,通过引用来互相关联起来。而ArrayLIst需要移动元素,故效率低。
ArrayList
没有向集合添加任何元素的时候,容量为0, 第一次add数据的时候,初始容量就是10,然后按照1.5倍扩容(10->15->22->...)
遍历(迭代器)
ListIterator
-
迭代器可以从前往后,也可以从后往前
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(10);
list.add(20);
list.add(30);
list.add(40);
list.add(50);
list.add(60);
ListIterator<Integer> integerListIterator = list.listIterator();
System.out.println("ListIterator从前往后遍历---》");
while (integerListIterator.hasNext()) {
System.out.println(integerListIterator.nextIndex()+"======="+integerListIterator.next());
}
System.out.println("ListIterator从后往前遍历---》");
while (integerListIterator.hasPrevious()) {
System.out.println(integerListIterator.previousIndex()+"======="+integerListIterator.previous());
}
}
输出
ListIterator从前往后遍历---》
0=======10
1=======20
2=======30
3=======40
4=======50
5=======60
ListIterator从后往前遍历---》
5=======60
4=======50
3=======40
2=======30
1=======20
0=======10
基本类型,自助装箱
subList
-
返回子集合
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(10);
list.add(20);
list.add(30);
list.add(40);
list.add(50);
list.add(60);
List<Integer> subList = list.subList(0, 3);
//下面打印 [10, 20, 30]
System.out.println(subList.toString());
}
remove
源码
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
//这里比较的是地址,所以删除对象的时候,只比较值的话,需要重写equal和hashCode
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
public boolean equals(Object obj) {
return (this == obj);
}
重写Student后,就可以通过对象删除了 list.remove(new Student("张三", 3));
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public static void main(String[] args) {
List<Student> list = new ArrayList<>();
Student s1 = new Student("张三", 3);
Student s2 = new Student("李四", 4);
Student s3 = new Student("王五", 5);
list.add(s1);
list.add(s2);
list.add(s3);
System.out.println(list.toString());
//删除
list.remove(new Student("张三", 3));
System.out.println("删除后(已经重写了Student的equal和hashCode)" + list.toString());
}
输出
[Student{name='张三', age=3}, Student{name='李四', age=4}, Student{name='王五', age=5}]
删除后(已经重写了Student的equal和hashCode)[Student{name='李四', age=4}, Student{name='王五', age=5}]
ArrayList部分源码
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
//默认初始容量
private static final int DEFAULT_CAPACITY = 10;
//实际元素的大小
private int size;
//存放元素的数组
transient Object[] elementData;
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//没有向集合添加任何元素的时候,容量为0, 第一次add数据的时候,初始容量就是10,然后按照1.5倍扩容(10->15->22->...)
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
*核心代码
*/
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);
}
}
Vector
public static void main(String[] args) {
Vector<String> vector = new Vector<>();
vector.add("A");
vector.add("B");
vector.add("C");
vector.add("D");
vector.add("E");
System.out.println(vector);
//遍历1
Iterator<String> iterator = vector.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + "\t");
}
System.out.println();
//遍历2 枚举器
Enumeration<String> elements = vector.elements();
while (elements.hasMoreElements()) {
System.out.print(elements.nextElement() + "\t");
}
}
LinkedList
双向链表
public static void main(String[] args) {
LinkedList<Student> linkedList = new LinkedList<>();
Student s1 = new Student("张三",3);
Student s2 = new Student("李四",4);
Student s3 = new Student("王五",5);
Student s4 = new Student("赵流六",6);
linkedList.add(s1);
linkedList.add(s2);
linkedList.add(s3);
linkedList.add(s4);
System.out.println(linkedList);
System.out.println("=====遍历:增强型for循环=======");
for (Student student : linkedList) {
System.out.println(student.toString());
}
System.out.println("=====遍历:迭代器=======");
Iterator<Student> iterator = linkedList.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable {
//实际元素的大小
transient int size = 0;
//链表头节点
transient Node<E> first;
//链表尾节点
transient Node<E> last;
//修改次数
protected transient int modCount = 0;
/**
* 空参数的构造由于生成一个空链表 first = last = null
*/
public LinkedList() {
}
public boolean add(E e) {
linkLast(e);
return true;
}
/**
*核心代码
*/
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);//当前节点的prev指向了上一个节点,第一次添加的时候prev节点为null
last = newNode;//链表尾节点last指向当前节点
if (l == null)
first = newNode;//第一次添加的时候,头节点first指向了当前添加的节点
else
l.next = newNode;//上个节点的next指向了当前节点
size++;
modCount++;
}
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;
}
}
}
Set
-
特点:无序,无下标,元素不可以重复
HashSet
-
存储接口:哈希表(数据+链表(单向链表)+红黑树)
-
存储过程
-
根据hashCode,计算保存的位置,如果此位置为空,则直接保存,如果不为空执行第二步
-
再执行equals方法,如果equals方法为true,则认为是重复,否则,形成链表
-
public static void main(String[] args) {
Set<Integer> set = new HashSet<>();
set.add(3);
set.add(4);
set.add(1);
set.add(2);
System.out.println(set);
System.out.println("====遍历====");
Iterator<Integer> iterator = set.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
重写hashCode和equal
源码分析
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable {
//HashSet实际使用HashMap的key储存元素的,value = new Object()
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
public HashSet() {
map = new HashMap<>();
}
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
}
TreeSet
-
基于排列顺序实现元素不重复
-
实现了SortedSet接口,对集合元素自动排序
-
元素对象的类型必须实现Comparable,指定排序规则
-
通过compareTo方法确定是否为重复元素
-
存储结构:红黑树
public class NewStudent implements Comparable<NewStudent> {
private String name;
private int age;
public NewStudent(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
验证
public static void main(String[] args) {
TreeSet<NewStudent> treeSet = new TreeSet<>();
NewStudent s1 = new NewStudent("zhangsan",13);
NewStudent s2 = new NewStudent("lisi",14);
NewStudent s3 = new NewStudent("wangwu",15);
NewStudent s4 = new NewStudent("zhaoliu",18);
NewStudent s5 = new NewStudent("zhaoliu",17);
//重复,添加不了
// NewStudent s6 = new NewStudent("zhaoliu",17);
treeSet.add(s1);
treeSet.add(s2);
treeSet.add(s3);
treeSet.add(s4);
treeSet.add(s5);
// treeSet.add(s6);
System.out.println(treeSet.size());
System.out.println(treeSet);
}
Comparator接口
public static void main(String[] args) {
TreeSet<Student> treeSet = new TreeSet<>(new Comparator<Student>(){
源码
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable {
//java.util.NavigableMap 可以看api帮助文档
private transient NavigableMap<E,Object> m;
private static final Object PRESENT = new Object();
public TreeSet(Comparator<? super E> comparator) {
this(new TreeMap<>(comparator));
}
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
}
Map
-
无序
-
存储键值对
-
键不能重复,值可以重复
实现类
-
HashMap
JDK1.2版本,线程不安全,运行效率快,允许null作为key或者value
-
HashTable(很少用)
JDK1.0版本,线程安全,运行效率慢,不允许null作为key或者value
-
Properties
HashTable的子类,要求key和value都是String。通常用于配置文件的读取
-
TreeMap
实现了SortedMap接口,可以对key自动排序
HashMap
-
存储结构:哈希表(数组+链表+红黑树)
-
使用key的hashCode和equals作为重复依据
entrySet效率要高于keySet
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>(16);
map.put("A", 1);
map.put("B", 2);
map.put("C", 3);
map.put("D", 1);
map.put("E", 1);
map.put("F", 1);
map.put("G", 1);
map.put("H", 1);
System.out.println(map);
System.out.println("========entrySet=========");
Set<Map.Entry<String, Integer>> entries = map.entrySet();
long start = System.currentTimeMillis();
for (Map.Entry<String, Integer> entry : entries) {
System.out.println(entry.getKey() + "=======" + entry.getValue());
}
long end = System.currentTimeMillis();
System.out.println("entrySet用时:" + (end - start));
System.out.println("========keySet=========");
Set<String> strings = map.keySet();
for (String string : strings) {
System.out.println(string + "=======" + map.get(string));
}
long end2 = System.currentTimeMillis();
System.out.println("keySet用时:" + (end2 - end));
}
源码
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
//默认初始容量 16
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
//最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;
//默认的加载因子
static final float DEFAULT_LOAD_FACTOR = 0.75f;
//树化阀值
static final int TREEIFY_THRESHOLD = 8;
//树还原链表阀值 当链表长度小于6的时候,红黑树会还原为链表
static final int UNTREEIFY_THRESHOLD = 6;
//最小树化容量 jdk1.8 当链表长度大于8 并且数组元素个数>=64,链表会转化为红黑树
static final int MIN_TREEIFY_CAPACITY = 64;
//数组+链表(单向链表,next)
transient Node<K,V>[] table;
//键值对的数量
transient int size;
//加载因子
final float loadFactor;
//下一次扩容的阀值
int threshold;
//节点(键值对),单向链表
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
//...
}
}
无参构造方法
public HashMap() {
//加载因子为默认加载因子0.75f
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
有参构造方法
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public HashMap(int initialCapacity, float loadFactor) {
//校验传入的容量
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
//传入的容量大于最大容量,就等于最大容量(2的30次方)
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
//校验加载因子
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
put
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
//计算key的hash值
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
//put值
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key