----------------------ASP.Net+Unity开发、.Net培训、期待与您交流! ----------------------
在Java中,集合是一组可变数量的数据项(也可能是0个)的组合,这些数据项可能共享某些特征,需要以某种操作方式一起进行操作。一般来讲,这些数据项的类型是相同的,或基类相同(若使用的语言支持继承)。列表(或数组)通常不被认为是集合,因为其大小固定,但事实上它常常在实现中作为某些形式的集合使用。集合的种类包括列表,集,多重集,树和图。枚举类型可以是列表或集。
一.综述
所有集合类都位于java.util包下。集合中只能保存对象(保存对象的引用变量)。(数组既可以保存基本类型的数据也可以保存对象)。
当我们把一个对象放入集合中后,系统会把所有集合元素都当成Object类的实例进行处理。从JDK1.5以后,这种状态得到了改进:可以使用泛型来限制集合里元素的类型,并让集合记住所有集合元素的类型(参见具体泛型的内容)。
Java的集合类主要由两个接口派生而出:Collection和Map,Collection和Map是Java集合框架的根接口,这两个接口又包含了一些接口或实现类。
Set和List接口是Collection接口派生的两个子接口,Queue是Java提供的队列实现,类似于List。
Set、List和Map可以看做集合的三大类。
List集合是有序集合,集合中的元素可以重复,访问集合中的元素可以根据元素的索引来访问。
Set集合是无序集合,集合中的元素不可以重复,访问集合中的元素只能根据元素本身来访问(也是不能集合里元素不允许重复的原因)。
Map集合中保存Key-value对形式的元素,访问时只能根据每项元素的key来访问其value。
对于Set、List和Map三种集合,最常用的实现类分别是HashSet、ArrayList和HashMap三个实现类。
二.List集合
List集合代表一个有序集合,集合中每个元素都有其对应的顺序索引。List集合允许使用重复元素,可以通过索引来访问指定位置的集合元素。
1、List接口和ListIterator接口
List作为Collection接口的子接口,可以使用Collection接口里的全部方法。List是有序集合,所以List集合里增加了一些根据索引来操作集合元素的方法:
◆void add(int index, Object element):将元素element插入在List集合的index处。
◆boolean addAll(int index, Collection c):将集合c所包含的所有元素都插入在List集合的index处。
◆Object get(int index):返回集合index索引处的元素。
◆int lastIndexOf(Object o):返回对象o在List集合中最后一次出现的位置索引。
◆Object remove(int index):删除并返回index索引处的元素。
◆Object set(int index, Object element):将index索引处的元素替换成element对象,返回新元素。
◆List subList(int fromIndex, int toIndex):返回从索引fromIndex(包含)到索引toIndex(不包含)处所有集合元素组成的子集合。
List集合可以根据索引来插入、替换和删除集合元素。
- public class ListDemo {
- /**list集合里方法的使用
- * @黑马ZWF
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- List l = new ArrayList(); //定义一个List新容器L
- l.add("张三"); //添加元素
- l.add("李四");
- l.add("王五");
- l.add("赵六");
- System.out.println(l); //输出l集合的所有元素
- l.remove(2); //移除第三个元素“王五”
- System.out.println(l); //输出移除元素后的l集合
- l.add(2,"孙七"); //在指定位置插入元素
- System.out.println(l); //输出添加元素后的l集合
- System.out.println(l.subList(1 , 3)); //输出截取位置的元素
- l.set(1, new String("宋八")); //替换第二个元素
- System.out.println(l); //输出替换元素后的集合
- }
- }
输出结果:
List集合可以根据索引来插入、替换和删除集合元素。 List集合可以使用普通for循环来遍历集合元素。List判断两个对象相等只要通过equals方法比较返回true即可。如在判断“”字符串的位置是,新创建了一个新字符串对象,但是程序仍返回第一次创建字符串对象的位置。当调用List的set(int index, Object element)方法来改变List集合指定索引处元素时,指定的索引必须是List集合的有效索引。
此外,list集合还提供了一个iterator()方法来遍历集合里的元素。代码示例:
- public class ListDemo2 {
- /**利用for循环和iterator方法来遍历集合元素
- * @黑马ZWF
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- List<String> l = new ArrayList<String>(); //定义List集合l
- l.add("张三"); //向集合添加元素
- l.add("李四");
- l.add("王五");
- l.add("赵六");
- for(int i = 0; i < l.size(); i++) { //利用for循环遍历l集合
- System.out.println(l.get(i));
- }
- System.out.println("`````````````````````````");
- Iterator<String> it = l.iterator(); //利用iterator()方法遍历元素
- while(it.hasNext()) { //迭代器中的方法hasNext()判断集合中,是否还有可取出的元素,如果有,就返回真,没有就返回假
- String s = it.next();
- System.out.println(s);
- }
- }
- }
2.ArrayList和Vector实现类
ArrayList和Vector作为List类的两个典型实现,完全支持前面介绍的List接口全部功能。
ArrayList和Vector类都是基于数组实现的List类,他们封装了一个动态再分配的Object[]数组。每个ArrayList或Vector对象有一个capacity属性,表示它们所封装的Object[]数组的长度。capacity会添加元素的个数而自动增加。当向集合中添加大量元素时,可以使用ensureCapacity方法一次性地增加capacity。这可以减少增加重分配次数,从而提供性能。capacity大小也可以在创建时就指定,该属性默认为10.
ArrayList和Vector提供如下两个方法来操作capacity属性:
◆void ensureCapacity(int minCapacity):将ArrayList或Vector集合的capacity增加minCapacity。
◆void trimToSize():调整ArrayList或Vector集合的capacity为列表当前大小。程序可调用该方法来减少ArrayList或Vector集合对象存储空间。
ArrayList和Vector用法几乎相同,Vector是一个古老的集合(从JDK1.0),起初Java还没有提供系统的集合框架,所以Vector里提供了一些方法名很长的方法:例如addElement(Object obj), 等同于add()方法。从JDK1.2以后,Java提供了系统的集合框架,就将Vector改为实习List接口,作为List的实习之一,从而导致Vector里有一些功能重复的方法。Vector具有很多缺点,通常尽量少用Vector实现类。
ArrayList和Vector的区别:ArrayList是线程不安全的,多个线程访问同一个ArrayList集合时,如果有超过一条线程修改了ArrayList集合,则程序必须手动保证该集合的同步性。Vector集合则是线程安全的,无线程序保证该集合的同步性。因为Vector是线程安全的,所以Vector的性能比ArrayList的性能要低。实际上,即使保证List集合线程安全,同样不推荐使用Vector实现类。Collections工具类,可以将一个ArrayList变成线程安全的。
3.LinkedList实现类
List还有一个LinkedList的实现,它是一个基于链表实现的List类,对于顺序访问集合中的元素进行了优化,特别是当插入、删除元素时速度非常快。因为LinkedList即实现了List接口,也实现了Deque接口(双向队列),Deque接口是Queue接口的子接口,它代表一个双向列表,Deque接口里定义了一些可以双向操作队列的方法:
◆void addFirst(Object e):将制定元素插入该双向队列的开头。
◆void addLast(Object e):将制定元素插入该双向队列的末尾。
◆Iterator descendingIterator():返回以该双向队列对应的迭代器,该迭代器将以逆向顺序来迭代队列中的元素。
◆Object getFirst():获取、但不删除双向队列的第一个元素。
◆Object getLast(): 获取、但不删除双向队列的最后一个元素。
◆boolean offerFirst(Object e): 将指定的元素插入该双向队列的开头。
◆boolean offerLast(Object e): 将指定的元素插入该双向队列的末尾。
◆Object peekFirst(): 获取、但不删除该双向队列的第一个元素:如果此双端队列为空,则返回null。
◆Object peekLast():获取、但不删除该双向队列的最后一个元素:如果此双端队列为空,则返回null。
◆Object pollFirst():获取、并删除该双向队列的第一个元素:如果此双端队列为空,则返回null。
◆Object pollLast():获取、并删除该双向队列的最后一个元素:如果此双端队列为空,则返回null。
◆Object pop():pop出该双向队列所表示的栈中第一个元素。
◆void push(Object e):将一个元素push进该双向队列所表示的栈中(即该双向队列的头部)。
◆Object removerFirst():获取、并删除该双向队列的最后一个元素。
◆Object removeFirstOccurrence(Object o):删除该双向队列的第一次的出现元素o。
◆Object removeLast():获取、并删除该双向队列的最后一个元素。
◆Object removeLastOccurrence(Object o):删除该双向队列的最后一次出现的元素o。
从上面方法中可以看出,LinkedList不仅可以当成双向队列使用,也可以当成“栈”使用。同时,LinkedList实现了List接口,所以还被当成List使用。
代码示例:
- public class ListDemo3 {
- /**LinkedList类的方法使用
- * @黑马ZWF
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- LinkedList l = new LinkedList();
- l.offer("张三"); //将字符串元素加入队列的尾部
- l.push("李四"); //将一个字符串元素入栈
- l.offerFirst("王五"); //将字符串元素添加到队列的头部
- System.out.println(l);
- System.out.println(l.peekFirst()); //访问、并不删除队列的第一个元素
- System.out.println(l.peekLast()); //访问、并不删除队列的最后一个元素
- System.out.println(l.pop()); //采用出栈的方式将第一个元素pop出队列
- System.out.println(l); //下面输出将看到队列中第一个元素被删除
- System.out.println(l.pollLast()); //访问、并删除队列的最后一个元素
- System.out.println(l); //下面输出将看到队列中只剩下中间一个元素:李四
- }
- }
输出结果是:
三.set集合
1.Set接口的使用
Set集合里多个对象之间没有明显的顺序。具体详细方法请参考API文档(可见身边随时带上API文档有多重要),基本与Collection方法相同。只是行为不同(Set不允许包含重复元素)。
Set集合不允许重复元素,是因为Set判断两个对象相同不是使用==运算符,而是根据equals方法。即两个对象用equals方法比较返回true,Set就不能接受两个对象。
- public class SetDemo1 {
- /**对于set集合不能有重复元素的说明
- * @黑马ZWF
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- Set<String> l = new HashSet<String>();
- l.add(new String("张三")); //添加一个字符串对象
- boolean result = l.add(new String("张三")); //再次添加一个字符串对象,
- System.out.println(result); //因为两个字符串对象通过equals方法比较相等,所以添加失败,返回false
- System.out.println(l); //下面输出看到集合只有一个元素
- }
- }
输出结果为:
说明:程序中,l集合两次添加的字符串对象明显不是一个对象(程序通过new关键字来创建字符串对象),当使用==运算符判断返回false,使用equals方法比较返回true,所以不能添加到Set集合中,最后只能输出一个元素。
Set接口中的知识,同时也适用于HashSet、TreeSet和EnumSet三个实现类。
2.Hashset类
HashSet按Hash算法来存储集合的元素,因此具有很好的存取和查找性能。
HashSet的特点:
(1)HashSet不是同步的,多个线程访问是需要通过代码保证同步
(2)集合元素值可以使null。
HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode()方法返回值也相等。
代码示例:
- public class SetDemo2 {
- /**Hashset类创建集合的实现
- * @黑马ZZ
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- HashSet<Integer> hs = new HashSet<Integer>();
- hs.add(123);
- hs.add(113);
- hs.add(145);
- hs.add(146);
- Iterator<Integer> it = hs.iterator();
- while(it.hasNext()){
- Integer i = it.next();
- System.out.println(i);
- }
- }
- }
输出结果为:
- public class HasheSetDemo2 {
- /**Hashset去掉Set集合中的重复的元素,依据,依据是对象的哈希值hashCode方法的结果和equals方法
- * @黑马ZZ
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- HashSet<String> hs = new HashSet<String>();
- hs.add("张三");
- hs.add("李四");
- hs.add("李四");
- Iterator <String> it = hs.iterator();
- while(it.hasNext()) {
- String p = it.next();
- System.out.println(p);
- }
- }
- }
输出结果为:
3.TreeSet类
TreeSet是SortedSet接口的唯一实现,TreeSet可以确保集合元素处于排序状态(元素是有序的)。
TreeSet提供的几个额外方法:
◆Comparator comparator(): 返回当前Set使用的Comparator,或者返回null,表示以自然方式排序。
◆Object first():返回集合中的第一个元素。
◆Object last():返回集合中的最后一个元素。
◆Objiect lower(Object e):返回集合中位于指定元素之前的元素(即小于指定元素的最大元素,参考元素可以不是TreeSet的元素)。
◆Object higher(Object e):返回集合中位于指定元素之后的元素(即大于指定元素的最小元素,参考元素可以不需要TreeSet的元素)。
◆SortedSet subSet(fromElement, toElement):返回此Set的子集,范围从fromElement(包含大于等于)到toElement(不包含小于)。
◆SortedSet headSet(toElement):返回此Set的子集,由小于toElement的元素组成。
◆SortedSet tailSet(fromElement):返回此Set的子集,由大于或等于fromElement的元素组成。
代码示例:
- public class TreeSetDemo1 {
- /**Treeset集合的应用
- * @黑马ZWF
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- TreeSet<Integer> nums = new TreeSet<Integer>(); //向TreeSet中添加四个Integer对象
- nums.add(5);
- nums.add(2);
- nums.add(10);
- nums.add(-9);
- System.out.println(nums); //输出集合元素,看到集合元素已经处于排序状态
- System.out.println(nums.first()); //输出集合里的第一个元素
- System.out.println(nums.last()); //输出集合里的最后一个元素
- System.out.println(nums.headSet(4)); //返回小于4的子集,不包含4
- System.out.println(nums.tailSet(5)); //返回大于5的子集,如果Set中包含5,子集中还包含5
- System.out.println(nums.subSet(-3 , 4));//返回大于等于-3,小于4的子集。
- }
- }
输出结果为:
四.Map集合
Map没有继承Collection接口,其提供的是key到value的映射。Map接口不能包含相同的Key,每个key只能映射 种称为散死技术进行的处理,亲生一个散死码的整数值,散列码常用作一个偏移量,该偏移量对应分配给映射的内存区域的起始位置,从而来确定存储对象在映射中的存储位置。 Map集合包含Map接口和Map接口的所有实现类。
Map集合的特点:
1.Map接口位于java.util包下,实现Map的类是可用来存储键(Key)——值(Value)对的容器。
2.Map接口的实现类有HashMap和TreeMap等,底层分别使用哈希表和二叉树来实现存储。
3.此容器中存储的键——值对通过键来标识,所以键不能重复。
提供的常用方法有:
◆Object put(Object key,Object value);往容器中添加元素,键、值均为对象,由于键不能重复,如果添加的元素的key已存在,则覆盖value,以Object类型返回被覆盖的value;如果添加的元素的key不存在,则返回null。
◆Object get(Object key);获取容器中键为key的元素的value,以Object类型返回。如果不存在此key,则返回null。
◆Object remove(Object key); 从容器中移除键为key的元素,以Object类型返回该元素的value;如果无元素的键为key,则返回null,如果此映射允许 null 值,则返回 null 值并不一定 表示该映射不包含该键的映射关系;也可能该映射将该键显示地映射到 null。
◆boolean containsKey(Object key);//该容器是否包含键为key的元素。
◆boolean containsValue(Object value);//该容器是否包含值为value的元素。
◆int size();//该容器中键值对的个数;
◆boolean isEmpty();//是否为空
◆void putAll(Map t);//将t中的所有元素添加到该容器中
◆void clear();//清空
代码示例:
- public class MapDemo1 {
- /**Map集合key和values值的对应关系展示
- * @黑马ZWF
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- Map map = new HashMap(); //构建Map实例
- map.put("01","李同学"); //向集合中添加对象
- map.put("02","魏同学");
- Set set = map.keySet(); //构建map集合中的所有key对象的集合
- Iterator it = set.iterator(); //创建集合迭代器
- System.out.println("key集合中的元素:");
- while(it.hasNext()){ //遍历集合
- System.out.println(it.next());
- }
- Collection coll = map.values(); //构建map集合中所有values值集合
- it = coll.iterator();
- System.out.println("values集合中的元素:");
- while(it.hasNext()){ //遍历集合
- System.out.println(it.next());
- }
- }
- }
输出结果为:
Map接口的实现类
Map 接口常用的实现类有HashMap和TreeMap。
HashMap类实现的Map集合对于添加和删除映射关系效率更高。
而TreeMap中的映射关系存在一定的顺序。
HashMap类:基于哈表的map接口的实现,此实现提供所有可选的映射操作,并允许使用null值和nll键,但必须保证键的唯一性。
TreeMap 类:不仅实现了Map接口,还实现了java.util.SortedMap接口,因此集合中的映射关系具有一定的顺序性。它是根据对象按照一定的顺序排列的,因此不允许对象是null。
代码示例:
- public class MapDemo2 {
- /**利用TreeMap类指定一个字符串,计算出每个字符出现的次数
- * 黑马ZWF
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- String str = "abwcfrrgffcw";
- char[] ch = str.toCharArray();
- TreeMap<Character,Integer> tm = new TreeMap<Character,Integer>();
- for(int x = 0 ; x < ch.length ; x++){ //取出单个字符,作为键,到集合中获取值
- Integer i = tm.get(ch[x]); //如果值是null,没有这个键,将字符当做键,值是1存储到集合
- if(i == null){ //如果值不是null,有这个键,值+1存储到集合
- tm.put(ch[x],1);
- }
- if(i!=null){
- i++;
- tm.put(ch[x],i);
- }
- }
- Set<Character> set = tm.keySet();
- Iterator<Character> it = set.iterator();
- while(it.hasNext()){
- Character c = it.next();
- Integer value = tm.get(c);
- System.out.println(c+"出现了"+value+"次");
- }
- }
- }
输出结果为: