面向对象语言对事物的体现是对象的形式,为了方便操作多个对象,就对对象进行存储。集合应运而生。
集合特点:
1、只存储对象(存对象引用,内存地址值);
2、长度可变;
3、可存不同类型对象;
因为集合对数据的存储方式(数据结构)都有不同;故产生了很多集合;
各种集合不断向上抽取过程中,形成了一个Collection接口,这个接口中有所有集合的共性。
-------------------------------------------------------------
Collection:
|--List:元素有序,可以重复,此集合体系有索引。
|--ArrayList:底层是数组结构;特点:查询速度快,但增删稍慢;线程不同步;效率高;
|--LinkedList:底层使用链表数据结构;特点:增删快,查询慢;
|--Vector:与ArrayList一样,但线程是同步的;被ArrayList取代了;用枚举取出其中元素;
|--Set:元素无序(存入取出的顺序不一定一致),元素不可重复;
|--Map:一次存俩元素,存在映射关系;每个键映射一个值,键要保证唯一;
共性方法:
1、添加元素-->boolean add(E e):参数类型是Object,E代表任意类型元素,是JDK1.5之后出现的新特性--泛型。
2、获取个数-->int size():集合长度;
3、删除元素-->boolean remove(Object o):删除单个元素;
-->clear():清空集合
4、判断元素-->boolean contains(Object o):判断o是否存在
-->boolean isEmpty():不包含元素返回true;
retainAll(Collection c);取交集
removeAll(Collection c);移除c中也有的元素
addAll(Collection c);将c中所有元素添加到调用方法的集合中;
containsAll(Collection c);调用者身上有c中全部元素,返回true;
Iterator iterator();返回此集合的迭代器,用于取出集合中元素;
------------------------------------------------------------
一、迭代器Itertor:这是什么?-->取出元素的方式。每个集合的数据结构不一样,存取方式也是不一样的,每个集合
中都将取出的动作封装成一个内部类,里面就封装了取出判断移除的方法。这样做就可以直接访问集合内的元素。
通过集合中的方法来获得对象:Iterator it = 集合引用.iterator();
Iterator中有三个方法:
boolean hasNext():若还有元素可以迭代就返回true;
E naxt():返回迭代下一个元素;
void remove():移除迭代器返回的最后一个元素;
可用结合for循环取出全部元素;如下
for(Iterator it=集合引用.iterator();it.hasNxet();)
{
it.next();
//此时如果元素有偶数个,就不会异常,但是有奇数个的话,就会抛出异常;
System.out.println(it.next()+it.next());
}
用循环时要注意,每次判断最好只取一次,取多次会有java.util.NoSuchElementException异常。
-------------------------------------------------------------
二、泛型:JDK1.5出现的新特性。用于解决安全问题的安全机制。集合框架中很常见。
使用格式:通过<>来定义要操作的引用数据类型;<>就是用来接收类型的;
好处:
1、将运行时期出现的问题ClassCastException转移到编译时期,方便程序员解决问题,让运行时更加安全,问题更少。
2,、避免了强制转换;
-----
泛型类:当类中要操作的引用数据类型不确定时,早期定义Object来完成扩展,现在定义泛型来完成扩展;
明确具体类型后,类中所有方法操作类型都确定了。
例:
//定义一个Tool类
class Tool<HH>//通过<>来定义,HH随意定义,意为引用数据类型;
{
private HH h;//引用数据类型引用
public HH getHH(){//获取引用数据类型引用
return h;
}
}
泛型方法:方法返回值前加<>;类上不加<>了。
静态方法泛型:不能访问类上定义的泛型;可定义在方法上;
-----
泛型限定:通配符“?”,上限:<? extends E>--->可以接受E类型或者E的子类型;
下限:<? super E>--->可以接受E类型或E类型的子类;(TreeSet集合中有个构造函数是这样干的)
-----------------------------------
三、List:元素有序,可以重复,此集合体系有索引。
体系下特有方法:所有可以操作角标的方法都是此体系特有方法。
增--> add(index,element);指定位置插入指定元素
boolean addAll(index,Collection);指定位置开始,将指定集合中所有元素插入列表
删--> remove(index);
改-->set(index,element);将指定角标的元素修改为element
查--> get(index);返回指定角标的元素可用for循环得到
List<E> subList(from,to);获得从from开始到to元素
listIterator():迭代时不能用集合对象的方法操作集合中元素,因为会发
生ConcurrentModificationException异常,此时List体系中有个ListIterator子接口,
可以对集合进行更多操作。只能通过List集合的listIterator方法获得对象。
多的方法:hasPrevious()-->逆向遍历;
set(E e);指定元素替换返回的最后一个元素。等等
List集合判断集合是否相同,依据的是元素的equals方法。所以很多时候要重写自定义的类的这个方法;除了contains调用元素equals方法,remove也调用了。
ArrayList类代码参考:
class ArrayListListDemo
{
public static void main(String[] args)
{
ArrayList al1 = new ArrayList();
ArrayList al2 = new ArrayList();
//集合al2中添加三个元素;
al2.add("java1");
al2.add("java2");
al2.add("java3");
//集合al1中添加元素;
al1.add("haha1");
al1.add(1,"haha3");//指定位置插入元素;
al1.addAll(1,al2);//将集合al2中的元素插入al1中,从1角标开始;
al1.set(2,"nihao");//将角标为2的元素修改成“nihao”;
al1.get(3);//获得角标3的元素;
al1.subList(2,5);//获得[2,5)的元素;
}
}
---------------------------
LinkedList:
特有方法:
addFirst():在集合头部添加---getFirst();获取第一个
getLast():在集合后面添加---getLast();获取最后一个
removeFirst();removeLast();获取第一个和最后一个,并删除集合中的这个元素;
以上get和remove在调用时,集合中没有元素,将出现NoSuchElementException异常;
在JDK1.6中出现了替代方法:offerFirst、peekFirst和pollFirst。没有元素时,返回null;
-----------------------------------
四、Set:元素无序(存入取出的顺序不一定一致),元素不可重复;
功能与Collection一致。底层使用Map集合。
-------------------------------
HashSet:底层数据结构是哈希表;按哈希值来存元素的。
它是如何保证元素唯一性呢?通过元素两个方法:hashCode和equals;
如果元素HashCode值相同,才会判断equals是否true;不同不调用。
判断删除依据:依赖方法是元素的hashcode和equals方法;
重写hashCode和equals方法代码示例:
//自定义一个Person类,重写了hashCode和equals方法
class Person
{
private int age;
private String name;
public Person(int age,String name){
this.age = age;
this.name = name;
}
//重写hashCode方法
public int hashCode(){
//HashSet集合判断首要条件,相同时就调用重写后的equals方法判断次要条件;
return this.name.hashCode()+age*14;
}
//重写equals方法
public boolean equals(Object o){
if(!(o instanceof Person))
throw new ClassCastException("类型不匹配!");
Person p = (Person)o;
return this.name.equals(p.name) && this.age == p.age;
}
}
-------------------------------
TreeSet:可对Set集合中元素进行排序;排序时,主要条件一样,要判断次要条件。
二叉树数据结构:小的放左边,大的放右边(左小右大)。取的时候从最小开始取。
保证元素唯一性的依据是:元素的compareTo方法return 0;
第一种排序方式:让元素自身具备比较性,即元素需要实现Comparable接口,并覆盖
compareTo方法。这中方式成为元素的自然排序。
第二种排序方式:当元素自身不具备或所具比较性不是所需,这时就让集合自身具备比较性;
定义比较器(类),实现Comparator接口,实现compare方法;集合在初始化时传入比较器对象。
两种方式代码示例:
//自定义一个Person类,实现了Comparable接口中compareTo方法;让元素具备比较性
class Person implements Comparable
{
private int age;
private String name;
public Person(int age,String name){
this.age = age;
this.name = name;
}
//实现compareTo方法,定义比较方式
public int compareTo(Object obj){
if (!(obj instanceof Person))
throw new ClassCastException("类型错误!");
Person p = (Person)obj;
if(this.age > p.age)
return 1;
if(this.age == p.age)
//判断次要条件
return this.name.compareTo(p.name);
return -1;
}
public int getAge(){ return age; }
public String getName(){return name; }
}
//定义一个比较器,实现了接口中的compare方法
class com implements Comparator
{
public int compare(Object obj1,Object obj2){
if(!(obj1 instanceof Person && obj2 instanceof Person))
throw new ClassCastException("类型错误!");
Person p1 = (Person)obj1;
Person p2 = (Person)obj2;
//先判断name的顺序,再判断次要条件age;
int num = p1.getName().compareTo(p2.getName());
if(num==0){
return new Integer(p1.getAge()).compareTo(new Integer(p2.getAge()));
}
return num;
/*
if(p1.getAge() > p2.getAge())
return 1;//先判断age,在判断name;
if(p1.getAge() == p2.getAge())
return p1.getName().compareTo(p2.getName());//判断次要条件name
return -1;
*/
}
}
--------------------------------------------------------------------
五、Map:一次存俩元素,存在映射关系;每个键映射一个值,键要保证唯一;
|--Hashtable:底层哈希表数据结构,不可存null键null值,线程同步;效率低;
|--HashMap:底层也是哈希表数据结构,可存null键null值,线程不同步;jdk1.2开始有,效率高;
|--TreeMap:底层二叉树数据结构,线程不同步,可对map集合中的键进行排序。
共性方法:
1、添加
V put(K key,V value);添加键对值(添加时出现相同键,新键会覆盖旧的,put会返回被覆盖值)
2、删除
void clear();移除所有映射关系
3、判断:
boolean containsValue(Object value);
boolean containsKey(Object key);
isEmpty();
4、获取
V get(Object key);根据key获得value,可以根据是否返回null来判断一个键是否存在;
int size();获取长度;
Collection<V> values();获取map集合中所有的值
Map集合两种取出的特殊方法:
取出原理:将map集合转成set,再用迭代器迭代。
1、Set<K> keySet();键map中所有键存入Set集合,用Set集合的迭代器取出所有键,再用get获取每个键对应的值;
HashMap代码参考:
import java.util.*;
class MapDemo {
public static void main(String[] args) {
//创建一个HashMap集合,存储了字符串键对值
HashMap<String,String> hm = new HashMap<String,String>();
hm.put("04","Java04");
hm.put("02","Java02");
hm.put("01","Java01");
hm.put("03","Java03");
Set<String> setKey = hm.keySet();//获得hm中所有的键并存入集合setKey中
Iterator<String> it = setKey.iterator();//获得Set集合迭代器
while(it.hasNext()){
String str = hm.get(it.next());//每获得一个键就取出一值并打印
System.out.println(str);
}
}
}
2、Set<Map.Entry><K,V> entrySet();获得映射关系Map.Entry;将键值对取出当做一个元素存入set;这个方法里面封装了取出键值的方法:getKey()和getValue();
关于Map.Entry接口:是Map接口中的接口,算是内部接口,里面封装了取出键值和修改
值(V setValue(V value))等方法;这个关系时随着集合生成才有的,故内部类形式定义;
代码参考:
import java.util.*;
class MapDemo2 {
//创建一个HashMap集合,存储了字符串键对值
HashMap<String,String> hm = new HashMap<String,String>();
hm.put("04","Java04");
hm.put("02","Java02");
hm.put("01","Java01");
hm.put("03","Java03");
//获得hm集合中的键值关系Map.entry,并存入Set集合中
Set<Map.Entry<String,String>> setEn = hm.entrySet();
Iterator<Map.Entry<String,String>> itEn = setEn.iterator();
while(itEn.hasNext()){
Map.Entry<String,String> entry = itEn.next();//得到映射关系
String key = entry.getKey();//取出Key值
String value = entry.getValue();//取出value值
System.out.println(key+"-----"+value);
}
}
-----------------------------------
Collections:操作集合的工具类
static <T extends Comparable<? super T>> void sort(List<T>list):给List集合进行排序的方法;
static <T> void sort(List<T> list, Comparator<? super T> c):指定比较器进行排序;
static <T extends Object & Comparable<? super T>> T max(Collection<?extends T> coll):获得集合中最大元素;(同有min)
static <T> int binarySearch(List<? extends T> list, T key,Comparator<? super T> c):
reverse():反转指定列表中元素的顺序。
static <T> void fill(List<? super T> list, T obj) :用指定元素替换调用者全部元素;
static <T> Comparator<T> reverseOlder(Comparator<T> cmp):强行逆转自然排序;
static <T> Comparator<T> reverseOrder(Comparator<T> cmp):返回一个比较器,它强行逆转指定比较器的顺序。
Arrays:操作数组的工具类
asList():将数组变成List集合,可以使用集合思想和方法来操作数组中的元素;
但是,数组转成集合不能使用集合的增删方法,因为数组长度固定的;增删会发生java.lang.UnsupportedOperationException异常;
将基本数据类型数组转List集合时,这个数组会作为集合中的元素存在;数组中元素是对象时,就正常转成集合;
集合变数组:Collection接口中的toArray方法;创建数组时要调用集合的size获得长度来设置长度,在内存中最优。
为何将集合边数组?为了限定对元素的操作。不需要进行增删了。
------------
总结:
集合就是用来存储对象用的,方便管理;
集合与数组有几点区别:可用集合多种,每个集合可以存储不同类型的对象;
集合的长度是可变的,就是说可以存一个集合长度就变长一个单位;
且集合可用方法很多,相比数组丰富多了;
JDK1.5之前,程序员都是按照自己的主观去判断集合里面存的什么类型对象,这样很不安全,
于是“泛型”应运而生;泛型对集合中存储的元素进行限定,规定这个集合只能存储规定的类型
的对象;
除了Map集合,Set和List两大派系的集合都可以使用迭代器Iterator对集合进行迭代;
Map集合存储的是键对值,一个键只能对应一个值,一个值可以对应多个键;
List集合存储的元素可以重复,是个有序的集合,可以使用工具类Collections中的sort方法进行自然排序
Set集合中的方法和Collection中的方法是一致的;与List集合刚刚相反,元素不可重复,无序;
对于HashSet集合存储自定义类的对象,需要重写hashCode方法和equals方法,当hashCode一样时,才去调用元素的equals方法判断;
对于TreeSet集合,就需要实现接口Comparable并重写CompareTo方法,对集合元素进行排序,当元素的排序不是咱需要的时,可以自
己写一个比较器:定义一个类并实现Comparator,重写方法compare;
对于这么多的集合,改怎么选择呢?这得看有什么需求,需要有序,元素可能重复,那么就选用List集合;不许自然顺序,并不可重复,就选Set;
若需要存储键对值,那么必然首选Map集合;这三大派系的集合都各有特点,使用时选择最需要最方便的无疑是最好的。