java基础之集合类

时间:2021-08-20 11:43:33

1.HashMap/**

 * @author erqing 
*
*/
public class MapTest {

public static void main(String[] args) {

/* 初始化map */
Map<String, Integer> map = new HashMap<String, Integer>();
System.out.println("HashMap的初始值:" + map.size());
System.out.println("HashMap是否为空:" + (map.isEmpty() ? "是" : "<span style="font-family: Arial, Helvetica, sans-serif;">否")); </span>

/* 想map中添加元素 */
map.put("erqing", 1);
map.put("niuniu", 2);
map.put("egg", 3);
System.out.println(map.size());
;
System.out.println("HashMap是否为空:" + (map.isEmpty() ? "是" : "否"));

/* 遍历HashMap中的元素 */
Set<String> set = map.keySet();
for (String s : set) {
System.out.println(s + " " + map.get(s) + " " + "hashcode:"
+ s.hashCode());
}

/*检测是否含有某个Key*/
System.out.println(map.containsKey("egg"));

/*检测是否含有某个Value*/
System.out.println(map.containsValue(2));

/*打印hashCode*/
System.out.println(map.hashCode());
}
}


用例:
  1. /** 
  2.  * 打印在数组中出现n/2以上的元素 
  3.  * 利用一个HashMap来存放数组元素及出现的次数 
  4.  * @author erqing 
  5.  * 
  6.  */  
  7. public class HashMapTest {  
  8.       
  9.     public static void main(String[] args) {  
  10.           
  11.         int [] a = {2,3,2,2,1,4,2,2,2,7,9,6,2,2,3,1,0};  
  12.           
  13.         Map<Integer, Integer> map = new HashMap<Integer,Integer>();  
  14.         for(int i=0; i<a.length; i++){  
  15.             if(map.containsKey(a[i])){  
  16.                 int tmp = map.get(a[i]);  
  17.                 tmp+=1;  
  18.                 map.put(a[i], tmp);  
  19.             }else{  
  20.                 map.put(a[i], 1);  
  21.             }  
  22.         }  
  23.         Set<Integer> set = map.keySet();  
  24.         for (Integer s : set) {  
  25.             if(map.get(s)>=a.length/2){  
  26.                 System.out.println(s);  
  27.             }  
  28.         }  
  29.     }  
  30. }  
2. ArrayList
  1. /** 
  2.  *  
  3.  * 关于ArrayList的基本操作  
  4.  * 其它操作感兴趣的读者可以自己结合源码实现一下 
  5.  *  * @author erqing 
  6.  *  
  7.  */  
  8. public class ListTest {  
  9.   
  10.     public static void main(String[] args) {  
  11.   
  12.         /* 新建一个ArrayList */  
  13.         ArrayList<String> list = new ArrayList<String>();  
  14.         System.out.println("初始化大小:" + list.size());  
  15.   
  16.         /* 添加元素 */  
  17.         list.add("zzz");  
  18.         list.add("egg");  
  19.         list.add("hell");  
  20.         list.add("child");  
  21.         System.out.println("当前容量:" + list.size());  
  22.   
  23.         /* 将ArrayList的大小和实际所含元素的大小设置一致 */  
  24.         list.trimToSize();  
  25.   
  26.         /* 遍历 */  
  27.         for (String string : list) {  
  28.             System.out.println(string);  
  29.         }  
  30.   
  31.         /* 在指定位置插入元素 */  
  32.         list.add(2"zhu");  
  33.   
  34.         for (String string : list) {  
  35.             System.out.println(string);  
  36.         }  
  37.   
  38.         System.out.println("--------------");  
  39.   
  40.         /* 清空list */  
  41.         list.clear();  
  42.   
  43.         /* 遍历 */  
  44.         for (String string : list) {  
  45.             System.out.println(string);  
  46.         }  
  47.         System.out.println("--------------");  
  48.     }  
  49. }  
3. LinkedList  底层采用 双向循环列表 实现,进行插入和删除操作时具有较高的速度,我们还可以使用LinkedList来实现队列和栈

  1. public class LinkedListTest {  
  2.       
  3.     public static void main(String[] args) {  
  4.   
  5.         /* 新建一个list */  
  6.         LinkedList<Integer> list = new LinkedList<Integer>();  
  7.         System.out.println(list.size());  
  8.   
  9.         /* 向list中添加元素 */  
  10.         list.add(222);  
  11.         list.add(111);  
  12.         list.add(0);  
  13.         list.add(3333);  
  14.         list.add(8888);  
  15.           
  16.         System.out.println(list.size());  
  17.   
  18.         /* 遍历list */  
  19.         for (Integer integer : list) {  
  20.             System.out.println(integer);  
  21.         }  
  22.   
  23.         /* 获取第一个元素 ,即header的next域*/  
  24.         System.out.println("第一个元素是:" + list.getFirst());  
  25.           
  26.         /*获取最后一个元素,即header的previous域*/  
  27.         System.out.println("最后一个元素是:"+list.getLast());  
  28.     }  
  29. }  
4. WeakHashMap

理解该集合类之前,WeakHashMap多用于缓存系统,就是说在系统内存紧张的时候可随时进行GC,但是如果内存不紧张则可以用来存放一些缓存数

据。因为如果使用HashMap的话,它里面的值基本都是强引用,即使内存不足,它也不会进行GC,这样系统就会报异常


5.集合类面试

两种:一、总体介绍下集合类有哪些。这个问题只要把我上文中的图介绍一下就行了。二、比较一下XXX和XXXX。

1、HashMap和HashTable

      相同点:二者都实现了Map接口,因此具有一系列Map接口提供的方法。

      不同点:

            1、HashMap继承了AbstractMap,而HashTable继承了Dictionary。

            2、HashMap非线程安全,HashTable线程安全,到处都是synchronized关键字。

            3、因为HashMap没有同步,所以处理起来效率较高。

            4、HashMap键、值都允许为null,HashTable键、值都不允许有null。

            5、HashTable使用Enumeration,HashMap使用Iterator。

推荐使用HashMap,因为她提供了比HashTable更多的方法,以及较高的效率,如果大家需要在多线程环境中使用,那么用Collections类来做一下同步即可

2、Set接口和List接口

  相同点:都实现了Collection接口

    不同点:

          1、Set接口不保证维护元素的顺序,而且元素不能重复。List接口维护元素的顺序,而且元素可以重复。

       2、关于Set元素如何保证元素不重复,我将在下面的博文中给出。

3、ArrayList和LinkList

     相同点:都实现了Collection接口

      不同点:ArrayList基于数组,具有较高的查询速度,而LinkedList基于双向循环列表,具有较快的添加或者删除的速度,二者的区别,其实就是数组和列表的区别。

4、SortedSetSortedMap

      二者都提供了排序的功能。 来看一个小例子:

  1. public static void main(String[] args) {  
  2.           
  3.         SortedMap<String, Integer> map = new TreeMap<String, Integer>();  
  4.         map.put("zgg"1);  
  5.         map.put("erqing"3);  
  6.         map.put("niu"0);  
  7.         map.put("abc"2);  
  8.         map.put("aaa"5);  
  9.           
  10.         Set<String> keySet = map.keySet();  
  11.         for (String string : keySet) {  
  12.             System.out.print(map.get(string)+" ");  
  13.         }  
  14.     }  

输出:5 2 3 0 1

从结果看得出:SortedMap具有自动排序功能

5、TreeMapHashMap

     HashMap具有较高的速度(查询)TreeMap则提供了按照键进行排序的功能。

6、HashSetLinkedHashSet

     HashSet,为快速查找而设计的Set。存入HashSet的对象必须实现hashCode()equals()

     LinkedHashSet,具有HashSet的查询速度,且内部使用链表维护元素的顺序(插入的次序),于是在使用迭代器遍历Set时,结果会按元素插入的次序显示。

7、TreeSet和HashSet

     TreeSet: 提供排序功能的Set,底层为树结构 。相比较HashSet其查询速度低,如果只是进行元素的查询,我们一般使用HashSet

8、ArrayList和Vector

      同步性:Vector是线程安全的,也就是说是同步的,而ArrayList是线程序不安全的,不是同步的。

      数据增长:当需要增长时,Vector默认增长为原来一培,而ArrayList却是原来的一半

9、Collection和Collections

      Collection是一系列单值集合类的父接口,提供了基本的一些方法,而Collections则是一系列算法的集合。里面的属性和方法基本都是static的,也就是说我们不需要实例化,直接可以使用类名来调用。

6.常见问题

1、Set集合如何保证对象不重复

这儿我采用HashSet来实现Set接口,先看个例子:

  1. public static void main(String[] args) {  
  2.         Set<String> set = new HashSet<String>();  
  3.         String a = "hello";  
  4.         String b = "hello";  
  5.         String s = new String("hello");  
  6.         String s1 = new String("hello");  
  7.       
  8.         set.add(a);  
  9.         set.add(s);  
  10.         set.add(s1);  
  11.         set.add(b);  
  12.         System.out.println("size:"+set.size());  
  13.         for (String ss : set) {  
  14.             System.out.println(ss);  
  15.         }  
  16.     }  

输出:

size:1
hello

说明,Set集合不允许有重复出现的对象,且最终的判断是根据equals()的。其实原理是这样的:HashSet的底层采用HashMap来存放数据,HashMap的put()方法是这样的:

  1. public V put(K key, V value) {  
  2.       if (key == null)  
  3.           return putForNullKey(value);  
  4.       int hash = hash(key.hashCode());//----------1----------  
  5.       int i = indexFor(hash, table.length);//-----------2---------  
  6.       for (Entry<K,V> e = table[i]; e != null; e = e.next) {//-----------3---------  
  7.           Object k;  
  8.           if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {  
  9.               V oldValue = e.value;  
  10.               e.value = value;  
  11.               e.recordAccess(this);  
  12.               return oldValue;  
  13.           }  
  14.       }//------------------4--------------------   
  15.       modCount++;  
  16.       addEntry(hash, key, value, i);  
  17.       return null;  
  18.   }  

当向HashMap中添加元素的时候,首先计算元素的hashcode值,然后根据1处的代码计算出Hashcode的值,再根据2处的代码计算出这个元素的存储位置,如果这个位置为空,就将元素添加进去;如果不为空,则看3-4的代码,遍历索引为i的链上的元素,如果key重复,则替换并返回oldValue值

2、集合类排序问题

一种情况是集合类本身自带排序功能,如前面说过的TreeSet、SortedSet、SortedMap等,另一种就是本身不带排序功能,我们通过为需要排序的类实现Comparable或者Comparator接口来实现。

先来看两个例子,一个是实现Comparable的,一个是实现Comparator的,为了方便,我将类都写在了一个文件中。

  1. package com.xtfggef.list.test;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Arrays;  
  5. import java.util.Collections;  
  6. import java.util.List;  
  7.   
  8. @SuppressWarnings("unchecked")  
  9. public class ComparableTest {  
  10.     public static void main(String[] args) {  
  11.         // User[] users = { new User("egg", 23), new User("niuniu", 22),  
  12.         // new User("qing", 28) };   
  13.         // Arrays.sort(users);   
  14.         // for (User user : users) {   
  15.         // System.out.println(user.getName() + " " + user.getAge());  
  16.         // }   
  17.         List<User> users = new ArrayList<User>();  
  18.         users.add(new User("egg"23));  
  19.         users.add(new User("niu"22));  
  20.         users.add(new User("qing"28));  
  21.   
  22.         Collections.sort(users);  
  23.         for (User user : users) {  
  24.             System.out.println(user.getName() + " " + user.getAge());  
  25.         }  
  26.     }  
  27.   
  28. }  
  29.   
  30. @SuppressWarnings("unchecked")  
  31. class User implements Comparable {  
  32.     private String name;  
  33.     private int age;  
  34.   
  35.     public User(String name, int age) {  
  36.         super();  
  37.         this.name = name;  
  38.         this.age = age;  
  39.     }  
  40.   
  41.     public String getName() {  
  42.         return name;  
  43.     }  
  44.   
  45.     public void setName(String name) {  
  46.         this.name = name;  
  47.     }  
  48.   
  49.     public int getAge() {  
  50.         return age;  
  51.     }  
  52.   
  53.     public void setAge(int age) {  
  54.         this.age = age;  
  55.     }  
  56.   
  57.     @Override  
  58.     public int compareTo(Object o) {  
  59.         return this.age - ((User) o).getAge();  
  60.     }  
  61. }  
下面是实现Comparator接口的:

  1. package com.xtfggef.comparator.test;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Collections;  
  5. import java.util.Comparator;  
  6. import java.util.List;  
  7.   
  8. public class ComparatorTest {  
  9.   
  10.     public static void main(String[] args) {  
  11.         List<User> users = new ArrayList<User>();  
  12.         users.add(new User("egg"21));  
  13.         users.add(new User("niu"22));  
  14.         users.add(new User("gg"29));  
  15.         UserComparator comparator = new UserComparator();  
  16.         Collections.sort(users, comparator);  
  17.         for (User user : users) {  
  18.             System.out.println(user.getUsername() + " " + user.getAge());  
  19.         }  
  20.     }  
  21.   
  22. }  
  23. class User {  
  24.     private String username;  
  25.     private int age;  
  26.   
  27.     public User(String username, int age) {  
  28.         super();  
  29.         this.username = username;  
  30.         this.age = age;  
  31.     }  
  32.   
  33.     public String getUsername() {  
  34.         return username;  
  35.     }  
  36.   
  37.     public void setUsername(String username) {  
  38.         this.username = username;  
  39.     }  
  40.   
  41.     public int getAge() {  
  42.         return age;  
  43.     }  
  44.   
  45.     public void setAge(int age) {  
  46.         this.age = age;  
  47.     }  
  48. }  
  49. class UserComparator implements Comparator<User> {  
  50.   
  51.     @Override  
  52.     public int compare(User user1, User user2) {  
  53.         int age1 = user1.getAge();  
  54.         int age2 = user2.getAge();  
  55.         if (age1 < age2) {  
  56.             return 1;  
  57.         }  
  58.         return 0;  
  59.     }  
  60.   
  61. }  
通过上面的这两个小例子,我们可以看出,Comparator和Comparable用于不同的场景,实现对对象的比较从而进行排序。

总结为:

相同点:

    1、二者都可以实现对象的排序,不论用Arrays的方法还是用Collections的sort()方法。

不同点:

    1、实现Comparable接口的类,似乎是预先知道该类将要进行排序,需要排序的类实现Comparable接口,是一种“静态绑定排序”。

    2、实现Comparator的类不需要,设计者无需事先为需要排序的类实现任何接口。

    3、Comparator接口里有两个抽象方法compare()和equals(),而Comparable接口里只有一个方法:compareTo()。

    4、Comparator接口无需改变排序类的内部,也就是说实现算法和数据分离,是一个良好的设计,是一种“动态绑定排序”。

    5、Comparator接口可以使用多种排序标准,比如升序、降序等。


3、使用for循环删除元素陷阱

先来看看下面这个程序:

  1. public class Test {  
  2.   
  3.     public static void main(String[] args) {  
  4.         List<String> list = new LinkedList<String>();  
  5.         list.add("A");  
  6.         list.add("B");  
  7.         list.add("C");  
  8.           
  9.         for(int i=0; i<list.size(); i++){  
  10.             list.remove(i);  
  11.         }  
  12.           
  13.         for(String item:list){  
  14.             System.out.println(item);  
  15.         }  
  16.     }  
  17. }  
读者朋友们 可以先猜猜这个程序输出什么?按我们的思路,应该是输不出什么,但是执行它,输出的却是:B。这是为什么呢?我们分部分析下这个程序,当地一步remove完后,集合内还剩2个元素,此时i为1,而list.size()的值为2,从0开始的话,i为1时,正好指向第二个元素,也就是说当remove完A后,直接就跳到C,将B漏了

解决办法:

  1. public class Test {  

    public static void main(String[] args) {
    List<String> list = new LinkedList<String>();
    list.add("A");
    list.add("B");
    list.add("C");

    for(int i=0; i<list.size(); i++){
    list.remove(i);
    i -= 1;//每次删除完后,i减少1
    }

    for(String item:list){
    System.out.println(item);
    }
    }
    }


7.集合的关系

java基础之集合类java基础之集合类

下面的表格也许可以更直接的表现出他们之间的区别和联系:

接口

简述

实现

操作特性

成员要求

Set

成员不能重复

HashSet

外部无序地遍历成员

成员可为任意Object子类的对象,但如果覆盖了equals方法,同时注意修改hashCode方法。

TreeSet

外部有序地遍历成员;附加实现了SortedSet, 支持子集等要求顺序的操作

成员要求实现caparable接口,或者使用 Comparator构造TreeSet。成员一般为同一类型。

LinkedHashSet

外部按成员的插入顺序遍历成员

成员与HashSet成员类似

List

提供基于索引的对成员的随机访问

ArrayList

提供快速的基于索引的成员访问,对尾部成员的增加和删除支持较好

成员可为任意Object子类的对象

LinkedList

对列表中任何位置的成员的增加和删除支持较好,但对基于索引的成员访问支持性能较差

成员可为任意Object子类的对象

Map

保存键值对成员,基于键找值操作,compareTo或compare方法对键排序

HashMap

能满足用户对Map的通用需求

键成员可为任意Object子类的对象,但如果覆盖了equals方法,同时注意修改hashCode方法。

TreeMap

支持对键有序地遍历,使用时建议先用HashMap增加和删除成员,最后从HashMap生成TreeMap;附加实现了SortedMap接口,支持子Map等要求顺序的操作

键成员要求实现caparable接口,或者使用Comparator构造TreeMap。键成员一般为同一类型。

LinkedHashMap

保留键的插入顺序,用equals 方法检查键和值的相等性

成员可为任意Object子类的对象,但如果覆盖了equals方法,同时注意修改hashCode方法。

IdentityHashMap

使用== 来检查键和值的相等性。

成员使用的是严格相等

WeakHashMap

其行为依赖于垃圾回收线程,没有绝对理由则少用

 

实现Collection接口的类,如Set和List,他们都是单值元素(其实Set内部也是采用的是Map来实现的,只是键值一样,从表面理解,就是单值),不像实现Map接口的类一样,里面存放的是key-value(键值对)形式的数据。这方面就造成他们很多的不同点,如遍历方式,前者只能采用迭代或者循环来取出值,但是后者可以使用键来获得值得值。