集合类存放于java.util包中。主要为Collection和Map接口,集合类里存放的对象都是指对对象的引用,并非对象本身。
Collection
java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的“子接口”如List和Set。(jdk1.6开始,为了防范因为类型不统一造成的ClassCastException异常,对集合框架进行升级加入泛型)。
Collection接口是单值存放的最大接口,可以向其中保存多个单值数据,但是在一般的开发中,往往很少直接使用Collection接口进行开发,基本上都是使用其子接口。Collection接口中的方法如下图所示:
Set接口
set接口是Collection的一个子接口,set集合存储的元素是无序的,不可重复的。
无序性:不是随机性,是指在底层的存储位置是无序的。
不可重复性:不允许集合中存在重复项,后添加的相同元素不能够添加进去。
Set接口最常见的实现类:HashSet,LinkedHashSet,TreeSet。
HashSet类
特点:不能保证元素的排列顺序;不是同步的,不是线程安全;集合值可以是null(最有允许一个哦)。
HashSet中使用哈希算法存储元素,因此具有很好的存取和查找性能:
当向Set中添加元素时,首先调用此对象所在类的hashcode()方法,计算此对象的hash值来决定此对象的存储位置。若此位置之前没有存储对象,则直接存储。若此位置已有对象,则比较equals()方法比较这两个对象是否相同,如果相同,则后一个不能存储进去。要求添加进Set中元素所在的类,一定要重写hashcode()方法和equals()方法
HashSet集合判断两个元素的相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode()方法返回值也相等。
import java.util.HashSet;
import java.util.Set;
class Person{
private String name;
private int age;
public Person(String name,int age){
this.name=name;
this.age=age;
}
public boolean equals(Object obj){//覆写equals()方法
if(this==obj){
return true;
}
if(!(obj instanceof Person)){
return false;
}
Person p=(Person)obj;
if(this.name.equals(p.name)&&this.age==p.age){
return true;
}else{
return false;
}
}
public int hashCode(){//覆写hashCode()方法
return this.name.hashCode()*this.age();
}
public toString(){
return "姓名"+this.name+"年龄"+age;
}
}
public class Demo1{
public static void main(String[] args){
Set<Person> mySet=new HashSet<Person>();
mySet.add(new Person("王五",30);
mySet.add(new Person("王五",30);
mySet.add(new Person("li",30);
System.out.println(mySet);
}
}
可以看出重复的项(王五 30)只出现一次。
LinkedHashSet类
LinkedHashSet是HashSet的子类,也不是线程安全的。
使用链表维护了一个添加进集合中的顺序(当我们遍历集合时,是按照添加进去的顺序实现的),改动较小,频繁遍历。
对于普通的插入删除操作,比HashSet慢,遍历会更快。
TreeSet类
TreeSet是SortedSet接口的实现类,是一种排序二叉树,TreeSet可以确保集合元素处于排序状态。
TreeSet支持两种排序方式,自然排序 和定制排序,其中自然排序为默认的排序方式。向TreeSet中加入的应该是同一个类的对象。
TreeSet判断两个对象不相等的方式是两个对象通过equals方法返回false,或者通过CompareTo方法比较没有返回0。向TreeSet中添加的应该是同一个类的对象,且最好是不可变对象。
1.自然排序
自然排序使用要排序元素的CompareTo(Object obj)方法来比较元素之间大小关系,然后将元素按照升序排列。
Java提供了一个Comparable接口,该接口里定义了一个compareTo(Object obj)方法,该方法返回一个整数值,实现了该接口的对象就可以比较大小。
obj1.compareTo(obj2)方法如果返回0,则说明被比较的两个对象相等,如果返回一个正数,则表明obj1大于obj2,如果是 负数,则表明obj1小于obj2。
2.定制排序
自然排序是根据集合元素的大小,以升序排列,如果要定制排序,应该使用Comparator接口,实现 int compare(T o1,T o2)方法,该方法用于比较o1和o2的大小:如果该方法返回正整数,则表示o1大于o2;如果方法返回0,则表示o1等于o2,如果该方法返回负整数,则表示o1小于o2。
List接口
list接口对Collection进行了简单的扩充。
返回值 | 方法 | 概要 |
---|---|---|
E | get(int index) | 返回类表中指定位置的元素。 |
E | set(int index, E element) | 用指定元素替换列表中指定位置的元素。 |
void | add(int index, E element) | 在列表中的指定位置插入元素 |
E | remove(int index) | 移除列表中指定位置的元素 |
int | indexOf(Object o) | 返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。 |
int | lastIndexOf(Object o) | 返回此列表中最后出现的指定元素的索引;如果列表不包含此元素,则返回 -1。 |
ListIterator | listIterator() | 返回此列表元素的列表迭代器(按适当顺序)。 |
ListIterator | listIterator(int index) | 返回列表中元素的列表迭代器(按适当顺序),从列表的指定位置开始。 |
List | subList(int fromIndex, int toIndex) | 返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分视图。 |
List接口中元素的特点为:
List中存储的元素实现类排序,而且可以重复的存储相关元素。两个常见的实现类为:ArrayList和LinkedList。
ArrayList
1)ArrayList:
ArrayList数组线性表的特点为:类似数组的形式进行存储,因此它的随机访问速度极快。
ArrayList数组线性表的缺点为:不适合于在线性表中间需要频繁进行插入和删除操作。因为每次插入和删除都需要移动数组中的元素。
- 如果在初始化ArrayList的时候没有指定初始化长度的话,默认的长度为10.
- ArrayList在增加新元素的时候如果超过了原始的容量的话,ArrayList扩容ensureCapacity的方案为“原始容量*3/2+1”。
- ArrayList是线程不安全的,切记Vector是ArrayList的多线程的一个替代品。
LinkedList
适合于在链表中间需要频繁进行插入和删除操作,但是随机访问速度较慢,因为底层内部是双向循环链表(使用内部类Entry存储节点的信息)的结构。
Vector
Vector基本上与ArrayList差不多,但是Vector是线程安全的。
当数组长度不够时,扩容方案为一倍。
Vector总是比ArrayList慢,应当尽量避免使用,效率很低。
Map接口
Map接口不是Collection接口的继承。主要存储“键值对”对象,根据键得到值,因此不允许键重复,但是允许值得重复。
HashMap
是最常用的Map,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度。HashMap最多只允许一条记录的键为Null;允许多条记录的值为 Null;HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap;可能会导致数据的不一致。如果需要同步,可以用 Collections的synchronizedMap方法使HashMap具有同步的能力。
LinkedHashMap保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的.在遍历的时候会比HashMap慢。
TreeMap
TreeMap不允许key的值为null。是非同步的。能够把它保存的记录根据键排序,默认是按升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。
Hashtable
Hashtable 与HashMap类似,不同的是:它不允许记录的键或者值为null;它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtale在写入时会比较慢。
几种遍历方法及对比
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("1", "value1");
map.put("2", "value2");
map.put("3", "value3");
//第一种:增强for循环,遍历keySet
for (String key : map.keySet()) {
System.out.println("key= "+ key + " and value= " + map.get(key));
}
//第二种 迭代器,entrySet迭代(性能较好)
Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> entry = it.next();
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
}
//第三种:增强for循环,entrySet迭代(性能一般)
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
}
//第四种 迭代器,keySet迭代(性能较好)
Iterator<Integer> iterator = map.keySet().iterator();
Integer key;
while (iterator.hasNext()) {
key = iterator.next();
map.get(key);
}
/*只取value*/
System.out.println("通过Map.values()遍历所有的value,但不能遍历key");
for (String v : map.values()) {
System.out.println("value= " + v);
}
}