集合类中默认可以存储任意数据类型
集合提供泛型机制,在定义时,最好为集合类提供一个明确的类型
Collection接口定义了单列集合中共有的功能
List:可以存储重复元素;List继承了Collection接口,有三个实现的类
ArrayList:数组列表,数据采用数组方式存储;底层是一个数组实现,提供一系列对
数组操作的方法;在第一次添加元素时创建底层数组,容量默认是10;在创建
ArrayList对象时,就会创建底层数组,容量是指定容量;ArrayList实现了长度可变的
数组,在内存中分配连续的空间,遍历元素和随机访问元素的效率比较高;查询快,
中间增加、删除慢。
//add方法
public boolean add(E e) {
ensureCapacityInternal(size + 1); //检查底层数组是否可以存储下元素
elementData[size++] = e;
return true;
}
private void ensureExplicitCapacity(int minCapacity) {
if (minCapacity - elementData.length > 0) //表示数组放不下了
grow(minCapacity); //对数组进行扩容
}
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); //新容量是原来长度的1.5倍
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity); //数组复制,创建一个新数组
}
ArrayList arrayList = new ArrayList<>();
arrayList.add(10); //向末尾添加元素,数组添加满了,自动扩容为原来的1.5倍
arrayList.add(0,10); //向指定的索引处添加元素
arrayList.add(20);
arrayList.add(20);
arrayList.add(20);
arrayList.add(20);
arrayList.add(20);
arrayList.clear(); //清空元素
arrayList.contains(10); //是否包含指定元素
arrayList.get(3);
/*E elementData(int index) {
return (E) elementData[index];
}*/
arrayList.indexOf(20); //返回此列表中指定元素的第一次出现的索引,如果此列表不包含元素,则返回-1
arrayList.isEmpty(); //如果此列表不包含元素,则返回true
arrayList.remove(10); //根据索引删除
arrayList.remove((Object)10); //根据内容删除
arrayList.set(0,20); //用指定的元素替换此列表中指定位置的元素
arrayList.size(); //实际存储元素的个数
Object[] objs = arrayList.toArray(); //以正确的顺序(从第一个到最后一个元素)返回一个包含此列表中所有元素的数组
System.out.println(Arrays.toString(objs));
System.out.println(arrayList);
LinkedList:链表,底层实现是双向链表;
一个一个的Node对象(data next(指针) prev(指针));插入、删除元素时效率比较
高;LinkedList中,提供了一些队和栈的操作方法;查询慢,中间增加、删除快。
LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("a");
linkedList.add("b");
linkedList.add("c");
linkedList.add("d");
linkedList.add(1,"e");
linkedList.addFirst("g");
System.out.println(linkedList.get(2));
/*public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
//在查找一个元素时,首先判断索引是否小于size/2,是的话从头节点开始向后查找,否则从为节点开始查找
//相比于ArrayList,查询效率低
LinkedList.Node<E> node(int index) {
if (index < (size >> 1)) {
LinkedList.Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
LinkedList.Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}*/
linkedList.addLast("e");
System.out.println(linkedList.pollFirst()); //取出表头的元素并删除
System.out.println(linkedList.remove());
linkedList.addLast("e");
System.out.println(linkedList.removeLast());
Vector:数组列表,底层实现也是数组,添加同步锁,是线程安全的
List集合遍历
for循环遍历
增强for循环的遍历
迭代器遍历(Iterator)
ListIterator<String> listIterator = list.listIterator();
while (listIterator.hasNext()) {
String str = listIterator.next();
System.out.println(str);
//listIterator.remove();
}
Set:不可以存储重复元素,Set中的元素是没有索引的
HashSet:底层使用哈希表,类中的元素不能重复,即彼此调用equals方法比较,都返回false,底层数据结构是哈希表+链表,哈希表依赖于哈希值存储。
TreeSet:底层使用树结构,可以给Set集合中的元素进行指定方式的排序,存储的对象必须实现Comparable接口,TreeSet底层数据结构是二叉树(红黑树是一种自平衡的二叉树)
HashSet/HashMap中如何保证值(键)不重复:当添加一个元素时,首先会调用值的hashCode方法,计算出一个hash值(整数),hash值不安全,有可能内容不一样,hash值一样,equals方法判断效率低;底层实现:首先会用内容计算出一个哈希值,用哈希值比较,一旦哈希值相等,就会使用equals方法对内容进行比较。
Map接口是将键(key)映射到值(value)的对象,一个映射不能包含重复的键,每个键最多只能映射到一个值;
Map接口是一种双列集合,存储的每个元素都由键和值两部分组成,一个键映射到一个值,键是不可以重复的,值可以重复;
HashMap集合是Map接口的一个实现类,它用于存储键值映射关系,但必须保证不出现重复的键,HashMap的键的类型一般锁定的是Sring类型;
//通过value获取key
public void getKey(Map map, String value) {
//通过entrySet()方法把map中的每个键值对变成对应成Set集合中的一个对象
Set set = map.entrySet();
Iterator<Map.Entry<String, String>> iterator = set.iterator();
while (iterator.hasNext()) {
//Map.Entry是一种类型,指向map中的一个键值对组成的对象
Map.Entry<String, String> entry = iterator.next();
if (entry.getValue().equals(value)) {
System.out.print(entry.getKey() + ",");
}
}
}
根据键值key计算hash值得到插入数组的索引 i
判断table[i]为空 ,直接new Node对象包含k,v,将Node对象存储到计算的位置即可
判断table[i]不为空,判断key是否存在,key如果存在,直接覆盖value
当计算的位置上已经有元素,判断链表长度,如果没有转为红黑树,直接将元素添加到链表中,如果已转为红黑树,那就添加到红黑树中
HashTable
Hashtable hashtable = new Hashtable();
hashtable.put("a", "a");
hashtable.put("c", "b");
hashtable.put("c", "c");
hashtable.put("d", "c");
hashtable.put("e", "e");
hashtable.put("f", "f");
System.out.println(hashtable);
System.out.println(hashtable.keySet());
System.out.println(hashtable.entrySet());//entrySet()只是单纯存储一组键值对
System.out.println(hashtable.values());
不能存储空值
HashTable适合多线程
HashMap适合单线程
Collections工具类
List<String> list = new ArrayList<String>();
Collections.addAll(list, "A", "B", "C", "D", "E", "E");
System.out.println(list);
Collections.reverse(list);//倒序
System.out.println(list);
Collections.reverse(list);
System.out.println(list);
Collections.shuffle(list);//随机排序
System.out.println(list);
Collections.sort(list);//
System.out.println(list);
Collections.swap(list, 1, 3);//交换位置
System.out.println(list);
Collections.replaceAll(list, "E", "X");//替换
System.out.println(list);
System.out.println(Collections.max(list));
System.out.println(Collections.min(list));
Collections.fill(list,"a");
System.out.println(list);
泛型
泛型有三种使用方式,分别为:泛型类、泛型接口和泛型方法。
保证了类型的安全性:在没有泛型之前,从集合中读取到的每一个对象都必须进行类型转换,如果不小心插入了错误的类型对象,在运行时的转换处理就会出错。
public static void useGeneric() {
ArrayList<String> names = new ArrayList<>();
names.add("add是一个方法");
names.add(123); //编译不通过
}
当重写为使用泛型时,代码不需要强制转换:
List<String> list = new ArrayList<String>();
list.add("hello");
String s = list.get(0); // no cast
子类也是泛型类,子类和父类的泛型类型要一致
class A<T> extends Demo<T>
子类不是泛型类,父类要明确泛型的数据类型
class A extends Demo<String>
public class GenericClass<T> {
private T value;
public GenericClass(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
类型通配符上限
类/接口<? extends 实参类型>
要求该泛型的类型,只能是实参类型,或实参类型的子类类型
类型通配符下限
类/接口<? super 实参类型>
要求该泛型的类型,只能是实参类型,或实参类型的父类类型