Java集合详解 Collection包
1.Collection接口下面是Collection接口的大概示意图:Collection接口,我们所熟悉的一些容器(集合)接口全部继承自它,比如List,Set,这些相对于我们之前用的数组,要厉害的多,但是这些集合有些底层也是用数组实现的。数组有下面的一些缺点:①长度固定②只能存储一种类型的对象③查询很快但是增删改很费劲Collection接口规定了一...
1.Collection接口
下面是Collection接口的大概示意图:
Collection接口,我们所熟悉的一些容器(集合)接口全部继承自它,比如List,Set,这些相对于我们之前用的数组,要厉害的多,但是这些集合有些底层也是用数组实现的。
数组有下面的一些缺点:
①长度固定
②只能存储一种类型的对象
③查询很快但是增删改很费劲
Collection接口规定了一些基本的对集合的操作,也就是说,这些最基本的操作,在之后所有的集合中都适用。
我们这里只看一些常用的方法:
size:返回集合的目前的大小
isEmpty:集合事后为空
contains:集合中是否包含指定元素
add:向集合中添加一个元素
remove:从集合中移除指定元素
containsAll:指定集合是否包含另一集合
addAll:将另一集合添加到指定集合中
removeAll:将指定集合与另一集合的共同元素移除
retainAll:返回两个集合的交集元素
——————————————————————————————————
它有两个子接口:List<>,Set<>
List<>:它有三个实现类,Vector,ArrayList,LinkedList。ArrayList底层实现原理是数组,所以它适合查询,不适合增删改;LinkedList底层是双向链表,适合增删改,不适合查询。它们的线程都不安全;Vector底层是数组,线程安全。List的特点是,加入的元素有顺序,可以重复。
ArrayList:这是最常用的一个实现类,我们比较常用这个,因为日常操作查询较多,增删操作比较少,因为底层是数组实现,所以多了很多方法是根据索引完成的,Collection的方法它都有,特有的有:
get(int):获取指定索引的元素
set(int,E):将指定索引的元素改变为指定元素
indexOf(Object):获取指定字符第一次出现位置
lastIndexOf(Object):获取指定字符最后一次出现的位置
add(int, E):在指定位置加入指定字符
remove(int):移除指定位置的字符
LinkedList:这是一个底层使用链表实现的集合,所以增删改极其方便,查询比较慢,它有许多链表特有的方法
addFirst(E e):在该列表开头插入指定的元素。
addLast(E e):将指定的元素追加到此列表的末尾。
getFirst():返回此列表中的第一个元素。
getLast():返回此列表中的最后一个元素。
offer(E e):将指定的元素添加为此列表的尾部(最后一个元素)。
boolean offerFirst(E e):在此列表的前面插入指定的元素。
boolean offerLast(E e):在该列表的末尾插入指定的元素。
peek():检索但不删除此列表的头(第一个元素)。
peekFirst():检索但不删除此列表的第一个元素,如果此列表为空,则返回 null
peekLast():检索但不删除此列表的最后一个元素,如果此列表为空,则返回 null
poll():检索并删除此列表的头(第一个元素)。
pollFirst():检索并删除此列表的第一个元素,如果此列表为空,则返回 null 。
pollLast():检索并删除此列表的最后一个元素,如果此列表为空,则返回 null
pop():从此列表表示的堆栈中弹出一个元素。
void push(E e):将元素推送到由此列表表示的堆栈上。
remove():检索并删除此列表的头(第一个元素)。
Set<>:Set接口与List接口类似,但是你加入的元素没有顺序,就是你加入1,2,3;但是你打印出来不一定是1,2,3。并且你无法加入重复元素,且只允许加入的一个值为null。它的常用实现类为HashSet<>,TreeSet<>。
HashSet<>:HashSet的底层实现是一个HashMap,我们先看一小段源码
private transient HashMap<E,Object> map;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
/**
* Constructs a new, empty set; the backing {@code HashMap} instance has
* default initial capacity (16) and load factor (0.75).
*/
public HashSet() {
map = new HashMap<>();
}
很明显它的底层是HashMap,将你想加入的值作为Key,而Value为new Object()。所以,Set中的值是不可重复的
下面我们来看它的常用方法,它的方法基本就和前面的一样了,记住它的无重复特性就好
TreeSet<>:TreeSet的底层是TreeMap实现的,我们都知道之前提到的HashSet是无序的,但是TreeMap默认从小到大给你排好序。
public static void main(String[] args) {
Set<Integer> s = new TreeSet<Integer>();
s.add(400);
s.add(100);
s.add(300);
s.add(200);
s.add(200);
System.out.println(s);
}
运行结果是:
我们可以看出来,结果是排好序的也是去重的。
那么问题来了,如果我们是一个自定义的对象,比如学生对象,我们想按成绩进行排序,怎么办呢??答案是:实现Comparable接口覆盖compareTo方法。
我们有这样一个需求:比较两个学生的成绩,成绩高的先输出,成绩相同的,id大的先输出。
public class Main{
public static void main(String[] args) {
Set<Student> s = new HashSet<Student>();
s.add(new Student(96, 1));
s.add(new Student(90, 4));
s.add(new Student(90, 2));
s.add(new Student(100, 5));
s.add(new Student(93, 3));
for(Student stu:s) {
System.out.println("stu "+stu.id+"------"+stu.grade);
}
}
}
class Student implements Comparable<Student>{
int grade;
int id;
public Student(int grade, int id) {
super();
this.grade = grade;
this.id = id;
}
@Override
public int compareTo(Student o) {//1:大于 0:等于 -1:小于
if(this.grade>o.grade) {
return 1;
}else if(this.grade<o.grade){
return -1;
}else {
if(this.id>o.id) {
return 1;
}
return -1;
}
}
}
我们可以看到这样的输出结果:
我们完成了两个对象的比较。HashSet是无序的,如果你一定要集合里元素的顺序与你加入的顺序一样,LinkedHashSet是一个很好的选择。这里我们不讲只演示一下。
public static void main(String[] args) {
Set<Integer> s = new LinkedHashSet<Integer>();
s.add(654);
s.add(200);
s.add(300);
System.out.println(s);
}
运行结果:
※2. Map<>接口:Map常用的实现类,HashMap<K, V>,TreeMap<K, V>,LinkedHashMap<K, V>
HashMap<K, V>:底层是哈希表结构,它以键值对的形式存在一个Key对应一个Value,哈希表!!!十分重要!!!!!哈希表的数据结构是“数组+链表”,JDK8以后变为了“数组+链表+红黑树”哈希表将数组与链表的优势完美结合,查询快,增删也快,我们先来看一下它的主要方法
containsKey(Object key):如果此映射包含指定键的映射,则返回 true 。
get(Object key):返回到指定键所映射的值,或 null如果此映射包含该键的映射。
isEmpty():如果此地图不包含键值映射,则返回 true 。
keySet():返回此地图中包含的键的Set视图。
put(K key, V value) :将指定的值与此映射中的指定键相关联。
remove(Object key) :从该地图中删除指定键的映射(如果存在)。
replace(K key, V value) :只有当目标映射到某个值时,才能替换指定键的条目。
但是我们要知道,你加入HashMap的元素是无序的,我们下面来深入了解一下HashMap,在源代码中,有这样一段代码,这是一个Node类,包含了你加入的Key和Value,还有一个hash值以及一个指向下一个节点的指针
这就是Node链表的基本结构
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
同时还有一个Node类型的数组,初始化长度为16,这个数组每一个节点里面,存的就是上面的链表(图略),只不过链表的长度是多少不确定
transient Node<K,V>[] table;
下面我们来说一说,为什么HashMap无序,对应下图
第一步当你put了一个Key时,会调用HashMap的hashcode方法得到一个哈希码,这时候通过hash函数计算得到一个值,这个hash()是什么呢,一般的常用的就是 hashcode%数组长度,这样我们得到的值一定是可以作为数组索引的值,计算出来是多少,就在对应数组索引处加入该对象,如果哈希函数算出来的值一样,那么就让上一个对象的next指针指向这个对象,于是就形成了HashMap的结构,数组里存储着链表,这就是为什么HashMap无序,因为你永远不知道hashcode算出来是多少,哈希函数计算出来的数组索引是多少,所以它的顺序不一定和你加入的顺序一样,如果要求一样,可以使用LinkedHashMap<>。JDK8以后,在每个数组中的链表长度超多8时,数据结构会由链表变为红黑树,也是提高查询性能的一种方法。
TreeMap<>: 就是你加入的Key输出时会为你从小到大排好序,如果是自定义对象依然要实现Comparable接口覆盖compareTo方法。才能实现对比自定义对象大小的功能。
如果一定要你加入的Key的顺序与你的输出顺序一致,可以使用LinkedHashMap。这里不做详解。
更多推荐
所有评论(0)