Java集合学习笔记
学习一个知识点我觉得有三点必须要弄明白,what(是什么)、why(为什么)、how(怎么做),至少要从这三个角度来思考问题,学习都是参考《java核心技术 卷1》这本书,这本书讲的很详细。
What:集合到底是什么?
Ans:为了解决程序设计过程中的存储问题,java设计人员开发了一组功能完善的数据结构。
How:怎样定义并实现它?
Ans:先定义规范(接口),再具体实现。
从开始学习前,必须要对现有的结合中的数据结构有一个宏观的了解,这个图是我从网上copy(感觉这个图有点问题,不过大致意思能够表达出来)下来的,自己画好麻烦。。。
数据结构知识基础回顾
我们学过哪些数据结构?
线性表
顺序表示:
链式表示:
线性链表
循环链表
双向链表
操作受限的线性表:
栈
队列
PS:栈和队列都有顺序和链式两种表示方式。
非线性结构:
树:
重点:二叉树
图
基本接口(最基础的规范)
Collection继承Iterator接口,则实现Collection接口的类都提供遍历的功能,还有add,remove,size等基本操作,集合还提供两外一个基本接口Map,但是保存的是键/值对。
子接口
Collection的两个重要的子接口:List,Set及Map接口,以及他们的常用实现类。
接口 | 实现类 |
List | ArrayList |
LinkedList | |
Set | HashSet |
TreeSet | |
Map | HashMap |
TreeMap |
List(线性表)
List不是链表,而是一张线性表,里面的元素可以重复,但是要按顺序排列着。
实现:
1)、ArrayList:数组列表,封装了一个动态再分配的对象数组。
2)、LinkedList,这个才是链表,而且是双向链接的,LinkedList.add方法是将对象添加到链表的尾部(因为LinkedList还实现了Deque接口),如果要添加到指定位置需要和迭代器配合使用
LinkedList类的常见使用:
public class A {
public static void main(String[] args) {
List<Integer> list = new LinkedList<>();
list.add(1);
list.add(2);
list.add(3);//1-->2-->3,先添加的元素在尾部
Iterator<Integer> listiterator = list.listIterator();
//遍历的使用
while(listiterator.hasNext()){
listiterator.next();//跳过第一个元素
if(listiterator.hasNext()){
listiterator.next();//删除第二个元素
listiterator.remove();
}
}
System.out.println(list);
}
}
两个类的区别:当我们要进行大量的删除、修改等操作的时候,要选用LinkedList这个类,对于链表来说,删除等更新操作速度很快,但是
不适宜使用大量查询操作,即使该类提供了get(int index)这个方法,而应该使用ArrayList类。
Set接口
Set是没有重复元素的无序的集合,add方法执行成功的前提条件是set中尚未存在该元素,更确切地讲,set不包含满足e1.equals(e2)的元素对e1和e2,并且最多包含一个 null 元素
实现:
1)、HashSet(散列集)
2)、TreeSet(树集)
HashSet散列集:
在学习HasSet之前,我们首先要搞明白一个概念,到底什么是哈希?
以每个对象的属性值为变量,通过一种函数为每个对象计算出一个函数值,把这个值解释为一块连续存储空间的单元地址(哈希码),更确切的说,具有相同数据域的对象将产生不同的散列码。
这么做有什么好处呢?为每个对象分配一个哈希码,如果两个对象的数据域都是相同的,但是在不同的物理空间上,我们应该处理呢?
使用散列码的最终目的是为了加快查找的速率,并分散对象,如果两个对象的数据域是相同的,那么就会产生哈希冲突,解决方式有链表法、开放地址法,在java里面采用的是前者。
即使产生冲突,我们查找对象的速度也依然很客观。最开始,我们提到,set是无序的集合,对于元素的访问是随机的,使用哈希可以帮助我们查找。
Ps:如果散列集中存储的对象的类型是我们自己定义的,我们就要重写hashcode方法。
TreeSet树集:
TreeSet类与散列集十分类似,不过比散列集有所改进,树集是一个有序集合,因为其实现了SortedSet接口。
SortedSet接口:
Comparator<? super E> comparator();
//返回对此 set 中的元素进行排序的比较器;如果此 set 使用其元素的自然顺序,则返回 null。
TreeSet的排序过程是如何实现的呢?
采用树结构(红黑树,呵呵,这个我也没学过。。。),每次添加元素到树中的时,都被放置在正确的位置上,这样迭代器就总是以排序好的顺序来访问每个元素,每次添加都要进行比较,所以添加元素的速率就要
相比较HashSet慢。
TreeSet中的元素是如何进行比较的呢?
在默认情况下,树集假定插入的元素实现了Comparable接口。
Public interface Comparable<T>{
Int compareTo(T other);
}
但是这样的实现真的好吗?让我们看一个具体实现:
Class Item implements Comparable<Item>{
Public int compareTo(Item other){
Return part-other.part;
}
}
Item类实现了这个接口,这一个TreeSet类中,我们按照这个顺序来排列,但是如果我们又定义了一个TreeSet,里面也保存的是Item类型的元素,但是我们不希望使用这个排序方式,这样我们就体现了这种的局限性。在这种情况下,我们可以通过Comparator(注意不是Comparable对象,方法也是不一样的)对象传递给TreeSet构造器来告诉树集应该使用什么样的比较方法。
TreeSet(Comparator<? super E> comparator)
Ps:在这里大家要区分Comparable与Comparator接口。
Map
映射表用来存放键/值对,如果提供了键,就能找到值,一个映射表中不能包含重复的键,每个键最多只能映射一个值。
Map接口提供三种视图,键集、值集、键值映射集,如下:
Set<K> keySet()
Collection<V> values()
Set<Map.Entry<K,V>> entrySet()
为什么要提供三种视图呢?
因为Map接口不是从Collection接口继承而来,没有迭代器,这几种视图都可以提供给我们遍历。
实现:
1)、HashMap
2)、TreeMap
HashMap,散列映射表,对键进行散列
TreeMap,树映射表,用键的整体顺序来对元素进行排序。
常规用法:
public class A {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("1", "a");
map.put("2", "b");
map.put("3", "c");
map.put("4", "d");
for(Map.Entry<String, String > entry:map.entrySet()){
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key+" "+value);
}
}
}
还有不少实体类,我没有一一介绍,只介绍了自己平时用的比较多的,还有几点没有写,Collections的用法、集合与数组之间的转换等,这里就不写了,回头写一篇这些集合类的具体实现。