第一部分
集合是Java存放对象的容器,存放在java.util包下,下图是Java集合类的继承与实现关系:
解释:
Collection接口是集合类的接口,List、Set、Queue的父接口。
- Set中不能包含重复的元素;
- List是一个有序的集合可以包含重复的元素,提供了按索引访问的方式。
Map是java.util包中的另一个接口,和Collection接口没有关系,相互独立。都属于集合类。
- Map包含了key-value对。
- Map不能包含重复的key,可以包含相同的value。
Iterator(迭代器),所有的集合类都实现了Iterator接口,这是一个用于遍历集合中元素的接口,主要包含三种方法:
- 1、hasNext():是否还有下一个元素;
- 2、next():返回下一个元素;
- 3、remove():删除当前元素
重要的类和接口介绍:
1、List(有序、可重复)
List关注的是索引,拥有一系列和索引相关的方法,查询速度快。
在向list集合中插入或删除数据时,会有后面数据的移动,所以插入删除数据速度慢。
1.1 ArrayList和LinkedList
- 在用法上区别不大,功能上有差别。LinkedList经常用在增删操作较多而查询操作很少的情况,ArrayList相反。
2、Set(无序、不能重复)
- Set理存放的对象是无序,不能重复的,集合中的对象不按特定的方式排序,只能简单地把对象加入集合中。
3、Map(键值对、键唯一、值不唯一)
Map集合中存储的是键值对,键不能重复,值可以重复。
根据键得到值,对map集合遍历时先得到键的set集合,对set集合进行遍历,得到相应的值。
3.1 Map的实现类:HashMap、HashTable、LinkedHashMap和TreeMap
3.2 HashMap
- 根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度;
- 遍历时,取得数据的顺序完全是随机的;
- 因为键对象不可以重复,所以HashMap最多只允许一条记录的键为Null,允许多条记录的值为null,是非同步的。
3.3 HashTable
- 和HashMap类似,是HashMap的线程安全版,它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtale在写入时会比较慢,他继承自Dictionary(字典)类,不同的是它不允许记录的键或者值为null,同时效率较低。
3.4 ConcurrentHashMap
- 线程安全,并且锁分离。ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的hashtable,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。
3.5 LinkedHashMap
- LinkedJHashMap保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的,在遍历时会比hashMap慢,有HashMap的全部特性。
3.6 TreeMap
- TreeMap实现了SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排列(自然顺序)也就是指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。不允许key值为空,非同步。
- 4、集合类的Iterator遍历:
Iterator it = arr.iterator();
while(it.hasNext()){
object o = it.next();
...
}
- 5、Map的遍历
- 第一种:KeySet()
- 将Map中所有的键存入到set集合中,因为set具备Iterator,所以迭代方式取出所有的键,再根据get方法。获取每一个键对应的值。keySet():迭代后只能通过get()取key。
- 取到的结果会乱序,是因为取得数据行主键的时候,使用了HashMap.keySet()方法,而这个方法返回的Set结果,里面的数据是乱序排放的。
- 第一种:KeySet()
典型用法:
Map map = new HashMap();
map.put("key1","lisi1");
map.put("key2","lisi2");
map.put("key3","lisi3");
map.put("key4","lisi4");
//先获取map集合的所有键的set集合,keyset()
Iterator it = map.keySet().iterator(); //获取迭代器
while(it.hasNext()){
Object key = it.next();
System.out.println(map.get(key));
}
- 第二种:entrySet()
- Set> entrySet() //返回此映射中包含的映射关系的 Set 视图。(一个关系就是一个键-值对),就是把(key-value)作为一个整体一对一对地存放到Set集合当中的。
- Map.Entry表示映射关系。entrySet():迭代后可以e.getKey(),e.getValue()两种方法来取key和value。返回的是Entry接口。
//典型用法
Map map = new HashMap();
map.put("key1","lisi1");
map.put("key2","lisi2");
map.put("key3","lisi3");
map.put("key4","lisi4");
//先获取map集合的所有键的set集合,keyset()
Iterator it = map.keySet().iterator(); //获取迭代器
while(it.hasNext()){
Entry e = (Entry)it.next();
System.out.println("键" + e.getKey() + "的值是:" + e.getValue());
}
entrySet()方法的效率更高一些!
对于keySet其实是遍历了2次,一次是转为iterator,一次就是从HashMap中取出key所对于的value。而entryset只是遍历了第一次,它把key和value都放到了entry中,所以快了。两种遍历的遍历时间相差还是很明显的。
主要实现类区别总结:
vector和ArrayList
1、vector是线程同步的,也是线程安全的;arrayList是线程异步的,是不安全的。如果不考虑到线程的安全因素,一般用arrayList效率比较高。
2、如果集合中的元素的数目大于目前集合数组的长度时,vector增长率为目前数组长度的100%,而arraylist增长率为目前数组长度的50%。如果在集合中使用数据量比较大的数据,用vector有一定的优势。
3、如果查找一个指定位置的数据,vector和arraylist使用的时间是相同的,如果频繁的访问数据,这个时候使用vector和arraylist都可以。而如果移动一个指定位置会导致后面的元素都发生移动,这个时候就应该考虑到使用linkedlist,因为它移动一个指定位置的数据时其它元素不移动。
4、ArrayList 和Vector是采用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,都允许直接序号索引元素,但是插入数据要涉及到数组元素移动等内存操作,所以索引数据快,插入数据慢,Vector由于使用了synchronized方法(线程安全)所以性能上比ArrayList要差,LinkedList使用双向链表实现存储,按序号索引数据需要进行向前或向后遍历,但是插入数据时只需要记录本项的前后项即可,所以插入数度较快。
arrayList和linkedList
1、arraylist是实现了基于动态数组的数据结构,LinkedList实现了基于链表的数据结构。
2、对于随机访问get和set,ArrayList觉得优先于Linked List,因为LinkedList要移动指针。
3、对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。 这一点要看实际情况的。若只对单条数据插入或删除,ArrayList的速度反而优于LinkedList。但若是批量随机的插入删除数据,LinkedList的速度大大优于ArrayList. 因为ArrayList每插入一条数据,要移动插入点及之后的所有数据。
HashMap和TreeMap
在Map 中插入、删除和定位元素,HashMap是最好的选择。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。使用HashMap要求添加的键类明确定义了hashCode()和 equals()的实现。
两个map中的元素一样,但顺序不一样,导致hashCode()不一样。
同样做测试:
- 在HashMap中,同样的值的map,顺序不同,equals时,false;
- 而在treeMap中,同样的值的map,顺序不同,equals时,true,说明,treeMap在equals()时是整理了顺序了的。
HashTable和HashMap
- 1、同步性:Hashtable是线程安全的,也就是同步,而HashMap是线程不安全的,不是同步的。
- 2、HashMap允许存在一个为null的key,多个为null的value 。
- 3、hashtable的key和value都不允许为null。
Java集合类图
整理出如下信息:
集合详解
HashSet
是Set接口的一个子类;
主要特点:
- 里面不能存放重复元素,元素的插入和输出顺序不一致
- 采用散列的存储方法,所以没有顺序
- 代码实例:
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
public class HashSetTest {
public static void main(String[] args) {
Set set = new HashSet();
set.add("a");
set.add("b");
set.add("c");
set.add("d");
set.add("f");
//插入重复元素,测试set是否可以存放重复元素
set.add("a");
set.add(null);
//插入重复null,看结果是否可以存放两个null
set.add(null);
Iterator iter = set.iterator();
System.out.println("输出的排列顺序为:");
while (iter.hasNext()){
System.out.println( iter.next());
}
}
}
HashSet存放的值无序切不能重复,可以存放null,但只能存放一个null值.
HashSet继承AbstractSet,有两个重要的方法,其中HashCode()和equals()方法,当对象被存储到HashSet当中时,会调用HashCode()方法,获取对象的存储位置。
HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode()方法返回值相等。
LinkedHashSet
- LinkedHashSet是HashSet的一个子类
- 只是HashSet底层用的HashMap,而LinkedHashSet底层用的LinkedHashMap
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
public class LinkedHashSetTest{
public static void main(String[] args){
Set set = new LinkedHashSet();
set.add("a");
set.add("b");
set.add("c");
set.add("d");
set.add("e");
System.out.println("LinkedHashSet存储值的排序为: ");
for(Iterator it = set.iterator();it.hasNext();){
System.out.println(it.next());
}
}
}
// 此时,LinkedHashSet中的元素时候有序的
SortSet(接口)
- sortSet是一个接口,里面(只有TreeSet这个实现可用)的元素一定是有序的。
- 保证迭代器按照元素递增顺序遍历的集合。可以按照元素的自然顺序(参见 Comparable)进行排序, 或者按照创建有序集合时提供的 Comparator进行排序。
//源代码:
public interface SortedSet<E> extends Set<E> {
//返回与此有序集合关联的比较器,如果使用元素的自然顺序,则返回 null。
Comparator<? super E> comparator();
//返回此有序集合的部分元素,元素范围从 fromElement(包括)到 toElement(不包括)。
SortedSet<E> subSet(E fromElement, E toElement);
//用一个SortedSet, 返回此有序集合中小于end的所有元素。
SortedSet<E> headSet(E toElement);
//返回此有序集合的部分元素,其元素大于或等于 fromElement。
SortedSet<E> tailSet(E fromElement);
//返回此有序集合中当前第一个(最小的)元素。
E first();
//返回此有序集合中最后一个(最大的)元素
E last();
}
TreeSet
- TreeSet类实现Set接口,该接口由TreeMap实例支持,此类保证排序后的set按照升序排列元素,根据使用的构造方法不同,可能会按照元素的自然顺序 进行排序(参见 Comparable或按照在创建 set 时所提供的比较器进行排序)。
- Set接口根据equals操作进行定义,但TreeSet实例将使用compareTo(或compare)方法执行所有的键比较。
import java.util.Comparator;
import java.util.TreeSet;
public class TreeSetTest{
static class Person{
int id;
String name;
int age;
public Person(int id, String name, int age){
this.id = id;
this.name = name;
this.age = age;
}
public String toString(){
return "id:" + this.id + " " + "name:" + this.name + " " + "age:" + this.age;
}
}
static class MyComparator implements Comparator<Person>{
@Override
public int compare(Person p1, Person p2){
if(p1 == p2){
return 0;
}
if(p1 !=null && p2 == null){
return 1;
}
else if(p1 == null && p2 !=null){
return -1;
}
if(p1.id > p2.id){
return 1;
}else if(p1.id < p2.id){
return -1;
}
return 0;
}
}
public static void main(String[] args){
MyComparator myComparator = new MyComparator();
TreeSet<Person> treeSet = new TreeSet<>(myComparator);
treeSet.add(new Person(3,"张三",20));
treeSet.add(new Person(2,"王二",22));
treeSet.add(new Person(1,"赵一",18));
treeSet.add(new Person(4,"李四",29));
//增加null空对象
treeSet.add(null);
System.out.println("TreeSet的排序是:");
for (Person p : treeSet){
if(p == null){
System.out.println(p);
}else {
System.out.println(p.toString());
}
}
}
}
实例用TreeSet保存对象引用,并且实现Comparator中compare方法进行比较和排序。
输出:
表明TreeSet是可以按照自定义方法中的比较进行排序的,且可以有空值。
Vector
- Vector 类也是基于数组实现的队列,代码与ArrayList非常相似。
- 线程安全,执行效率低。
- 动态数组的增长系数
- 由于效率低,并且线程安全也是相对的,因此不推荐使用vector
Stack
- 继承自vector类,栈
- 主要的方法:
- empty , boolean , 判空
- peek , E , 返回栈顶元素
- pop , E , 出栈
- push , E , 入栈
- search, E , 返回最靠近顶端的目标元素到顶端的距离
import java.util.Stack;
public class StackTest {
static class Person{
int id;
String name;
int age;
public Person(int id, String name, int age){
this.id = id;
this.name = name;
this.age = age;
}
public String toString(){
return "id:"+ this.id + " " + "name:" + this.name +" " + "age:" + this.age;
}
}
public static void main(String[] args) {
Stack stack = new Stack();
stack.push(new Person(1,"赵一",18));
stack.push(new Person(2,"王二",19));
stack.push(new Person(3,"张三",20));
stack.push(new Person(4,"李四",21));
System.out.println("栈顶元素是:(" + stack.peek() + ")");
System.out.println("目标元素离栈顶多少距离:" + stack.search(stack.get(0)));
System.out.println("栈元素从栈顶到栈底的排序是:");
//此处先用size保存是因为pop时,size会减1,
// 如果直接stack.size放在循环中比较,只能打印一半对象
int size = stack.size();
for (int i = 0; i < size ; i++) {
Person p = (Person) stack.pop();
System.out.println(p.toString());
}
}
}
HashMap
数组的特点是:寻址容易,插入和删除困难;
而链表的特点是:寻址困难,插入和删除容易。
哈希表结合了两者的优点。
哈希表有多种不同的实现方法,可以理解将此理解为“链表的数组”