集合做为一个工具类,日常撸代码的时候用的还是挺多的,尤其是做一些比赛题啥的,和String类都挺重要的
-
集合类的框架体系图
常用的一般有ArrayList,LinkedList,HashSet,HashMap
集合特性>
任何一个集合都可以使用Iterator进行迭代遍历,像List这种顺序存储的集合是含有get()方法,Set与Map就没有,要想进行遍历,只能用迭代器进行
一.List
List接口扩展自Collection,它可以定义一个允许重复的有序集合,从List接口中的方法来看,List接口主要是增加了面向位置的操作,允许在指定位置上操作元素,同时增加了一个能够双向遍历线性表的新列表迭代器ListIterator。
1.1ArrayList
/**
* Appends the specified element to the end of this list.
*添加制定的元素至该集合的尾部
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
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) {
//当前集合数组里面为空的时候,给定一个长度为10的数组
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
//DEFAULT_CAPACITY静态常量为10
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
//当要添加的元素添加之后长度要是大于当前集合数组的长度
//要对集合数组扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//扩容为之前长度的1.5倍
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);
}
1.2LinkedList
LinkedList是将元素以链表的方式进行存储,根据使用需要,选择效率较高的类型合适的集合使用
二.Set
Set集合与List集合最大的区别就是Set里面不能存放重复的元素,前提是该包装类里面必须重写equals()和HashCode()方法,
java自带的类中当然是已经写过的啦。
2.1 HashSet
当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据 hashCode值来决定该对象在HashSet中存储位置。
示例:给Set集合里面添加自己创建的类Student,自己重写内部equals()和HashCode()方法,以学生的id为判断标准(即set集合里最终不能含有重复id的元素)
import java.util.*;
public class Set测试 {
public static void main(String[] args) {
Set<Student> studentSet = new HashSet<>();
Student s1 = new Student(2011);
Student s2 = new Student(2034);
Student s3 = new Student(1022);
Student s4 = new Student(1022);
Student s5 = new Student(2011);
studentSet.add(s1);
studentSet.add(s2);
studentSet.add(s3);
studentSet.add(s4);
studentSet.add(s5);
Iterator<Student> iterator = studentSet.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next().getId());
}
}
}
import java.util.Objects;
public class Student {
private int id;
public Student() {
}
public Student(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return id == student.id;
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
2.2 TreeSet
树形集是一个有序的Set,其底层是一颗树,这样就能从Set里面提取一个有序序列了。在实例化TreeSet时,我们可以给TreeSet指定一个比较器Comparator来指定树形集中的元素顺序,或者自定义类实现Comparable接口,重写内部的compareTo方法。树形集中提供了很多便捷的方法。
而TreeSet支持两种排序方式,自然排序和定制排序,其中自然排序为默认的排序方式。向 TreeSet中加入的应该是同一个类的对象。TreeSet判断两个对象不相等的方式是两个对象通过equals方法返回false,或者通过CompareTo方法比较没有返回0。
示例>同样是添加Student进Set中,此次除重并按照学号大小进行排序
import java.util.Objects;
public class Student implements Comparable<Student> {
private int id;
public Student() {
}
public Student(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
@Override
public int compareTo(Student o) {
if (this.id > o.id) {
return 1;
} else if (this.id < o.id) {
return -1;
}
return 0;
}
}
import java.util.*;
public class Set测试 {
public static void main(String[] args) {
Set<Student> studentSet = new TreeSet<>();
Student s1 = new Student(2011);
Student s2 = new Student(2034);
Student s3 = new Student(1022);
Student s4 = new Student(1022);
Student s5 = new Student(2011);
studentSet.add(s1);
studentSet.add(s2);
studentSet.add(s3);
studentSet.add(s4);
studentSet.add(s5);
Iterator<Student> iterator = studentSet.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next().getId());
}
}
}
三.Map
数组的元素类型是Node<K,V>,Node<K,V>继承自Map.Entry<K,V>,表示键值对映射。
一个键对应一个值,值可以一样,但键必须唯一
Map接口有三个比较重要的实现类,分别是HashMap、TreeMap和HashTable。
TreeMap是有序的,HashMap和HashTable是无序的。
Hashtable的方法是同步的,HashMap的方法不是同步的。这是两者最主要的区别。
这就意味着:
Hashtable是线程安全的,HashMap不是线程安全的。
HashMap效率较高,Hashtable效率较低。
如果对同步性或与遗留代码的兼容性没有任何要求,建议使用HashMap。 查看Hashtable的源代码就可以发现,除构造函数外,Hashtable的所有 public 方法声明中都有 synchronized关键字,而HashMap的源码中则没有。
Hashtable不允许null值,HashMap允许null值(key和value都允许)
父类不同:Hashtable的父类是Dictionary,HashMap的父类是AbstractMap
3.1 TreeMap
存储为红黑树,也就是平衡排序二叉树
主要功能用于排序,与TreeSet一样,想要给自定义的类进行排序的话,还得实现Comparable接口,实现compareTo方法
示例>无序的学生id,按照从小到大的顺序排序,最后分别输出学号,姓名,学号和姓名
import java.util.*;
public class Map测试 {
public static void main(String[] args) {
Map<Student, String> studentStringMap = new TreeMap<>();
studentStringMap.put(new Student(20), "张三");
studentStringMap.put(new Student(30), "李四");
studentStringMap.put(new Student(10), "王二麻");
studentStringMap.put(new Student(5), "小卓子");
//输出所有的键
Iterator<Student> studentIterator = studentStringMap.keySet().iterator();
while (studentIterator.hasNext()) {
System.out.println(studentIterator.next().getId());
}
//输出所有的值
Iterator<String> stringIterator = studentStringMap.values().iterator();
while (stringIterator.hasNext()) {
System.out.println(stringIterator.next());
}
//输出键值所有
Iterator<Map.Entry<Student, String>> entryIterator = studentStringMap.entrySet().iterator();
while (entryIterator.hasNext()) {
Map.Entry<Student, String> temp = entryIterator.next();
System.out.println(temp.getKey().getId() + ":" + temp.getValue());
}
}
}
3.2 HashMap
通用的存储键值对的集合