java集合---Collection、Map、List、Set

一、集合与数组

     数组(可以存储基本数据类型)是用来存现对象的一种容器,但是数组的长度固定,不适合在对象数量未知的情况下使用。数组一旦定义,长度将不能再变化。

 集合(只能存储对象,对象类型可以不一样)的长度可变,可在多数情况下使用。

二、层次关系


三、几种常用的接口和类简介

  1. Collection和Iterator接口
Set和List接口继承了Collection接口
               在Collection接口中声明了适用于Set和List的通用方法:


               boolean add(Object o)          : 向集合中加入一个对象的引用;
               void clear()                   : 删除集合中的所有对象引用,即不再持有这些对象的引用;
               boolean contains(Object o)     : 判断在集合中是否持有特定对象的引用;
               boolean isEmpty()              : 判断集合是否为空;
               Iterator iterator()            : 返回一个Iterator对象,可用它来遍历集合中的元素;
               boolean remove(Object o)       : 从集合中删除一个对象的引用;
               int size()                     : 返回集合中元素的数目;
               Object[] toArray()            : 返回一个数组,该数组包含集合中的所有元素;

Iterator接口中的定义的方法可以帮我们去遍历集合中的元素。
               Iterator接口隐藏底层集合的数据结构,向客户程序提供了遍历各种类型的集合的统一方法。Iterator接口中声明方法:

               hasNext()                      : 判断集合中的元素是否遍历完毕,如没有,就返回true;
               next()                         : 返回下一个元素;
               //remove()                       : 从集合中删除上一个由next()方法返回的元素;

import java.util.*;
               public class Visitor {
                      public static void print(Collection c) {
                             Iterator it = c.iterator();
                             while(it.hasNext()) {
                                    Object element = it.next();
                                    System.out.println(element);
                             }
                      }

                      public static void main(String args[]) {
                             Set set = new HashSet();
                             set.add("Tom");
                             set.add("Mary");
                             set.add("Jack");
                             print(set);

                             List list = new ArrayList();
                             list.add("Linda");
                             list.add("Mary");
                             list.add("Rose");
                             print(list);

                             Map map = new HashMap();
                             map.put("M","男");
                             map.put("F","女");
                             print(map.entrySet());
                     }
               }
 2. Set
               最简单的一种集合,集合中的对象无序、不能重复。主要实现类包括:
         
               . HashSet      : 按照哈希算法来存取集合中的对象,存取速度比较快;
               . LinkedHashSet: HashSet子类,不仅实现Hash算法,还实现链表数据结构,链表数据结构能提高插入和删除元素的性能;
               . TreeSet      : 实现SortedSet接口,具有排序功能;


 一般用法:
               Set集合中存放的是对象的引用,并且没有重复对象。
               Set set = new HashSet();
               String s1 = new String("hello");
               String s2 = s1;
               String s3 = new String("world");
               set.add(s1);
               set.add(s2);
               set.add(s3);
               System.out.println(set.size());


               当一个新的对象加入到Set集合中时,
  Set的add方法是如何判断这个对象是否已经存在于集合中的呢?
  它遍历既存对象,
  通过equals方法比较新对象和既存对象是否有相等的。


               boolean isExist = false;
               Iterator it = set.iterator();
               while(it.hasNext()) {
                     String oldStr = it.next();
                     if(newStr.equals(oldStr)) {
                            isExists = true;
                            break;
                     }
               }


               举例:Set set = new HashSet();
                     String s1 = new String("hello");
                     String s2 = new String("hello");
                     set.add(s1);
                     set.add(s2);
                     System.out.println(set.size());                         //集合中对象数目为1;


               1) HashSet
                  
                  按照哈希算法来存取集合中的对象, 存取速度比较快。当向集合中加入一个对象时,
 HashSet会调用对象的hashCode()方法来获得哈希码,然后根据这个哈希码进一步计算出对象在集合中的存放位置。
 在Object类中定义了hashCode()方法和equals()方法,Object类的equals()方法按照内存地址比较对象是否相等,
 因此如果object.equals(object2)为true, 则表明object1变量和object2变量实际上引用同一个对象,
 那么object1和object2的哈希码也肯定相同。为了保证HashSet能正常工作, 
 要求当两个对象用equals()方法比较的结果为true时,它们的哈希码也相等。
 
 如果用户定义的Customer类覆盖了Object类的equals()方法,
 但是没有覆盖Object类的hashCode()方法,就会导致当
                  customer1.equals(customer2)为true时,
 而customer1和customer2的哈希码不一定一样,
 这会使HashSet无法正常工作。
 (先调用对象的hashCode()方法比较,
 如果是true再调用equals方法比较,
 如果还是true再认为俩个对象是同一个。
                  public class Customer {
                         private String name;
                         private int age;
                         public Customer(String name, int age) {
                                this.name = name;
                                this.age = age;
                         } 
                        public String getName() {
                                return name;
                         }
                         public int getAge() {
                                return age;
                         }
                         public boolean equals(Object o) {
                                if(this==o) return true;                          
                                if(!(o instanceof Customer)) return false;
                                Customer other = (Customer)o;

                                if(this.name.equals(other.getName()) && this.age==other.getAge())
                                      return true;
                                else 
                                      return false;


                         }
                  }
                  以下程序向HashSet中加入两个Customer对象。
                  Set set = new HashSet();
                  Customer customer1 = new Customer("Tom", 15);
                  Customer customer2 = new Customer("Tom", 15);
                  set.add(customer1);
                  set.add(customer2);
                  System.out.println(set.size());         //打印出 2

                  出现以上原因在于customer1和customer2的哈希码不一样, 因此为两为customer对象计算出不同的位置,于是把它们放到集中中的不同的地方。
                  应加入以下hashCode()方法: 
                  public int hashCode() {
                         return this.age;
                  }
               2) TreeSet
                  TreeSet实现了SortedSet接口,
 能够对集合中的对象进行排序。
 当TreeSet向集合中加入一个对象时,会把它插入到有序的对象序列中。那么TreeSet是如何对对象进行排序的呢?
 TreeSet支持两种排序方式:自然排序和客户化排序。默认情况下TreeSet采用的是自然排序方式:
                  a. 自然排序
                     在JDK类库中, 有一部分类实现了Comparable接口,如Integer、Double和String等。Comparable接口有一个compareTo(Object o)方法,它返回整数类型。对于x.comapreTo(y), 如
                     返回0,      表明   x和y相等
                     返回值大于0, 表明   x>y
                     返回值小于0, 表明   x<y
   **即:想表示出x比y大,让x.comapreTo(y)返回一个大于0的数字即可
                     TreeSet调用对象的compareTo()方法比较集合中对象的大小,然后进行【升序】排序,这种排序方式称为自然排序。


                     ------------------------------------------------------------------------------------------------
                     JDK类库中实现了Comparable接口的一些类的排序方式: 


                     Byte, Short, Integer, Long, Double, Float     :         按数字大小排序;
                     Character                                     :         按字符的Unicode值的数字大小排序;
                     String                                        :         按字符串中字符的Unicode值排序;
                     ------------------------------------------------------------------------------------------------

                     使用自然排序, TreeSet中只能加入相同类型对象,
且这些对象必须实现了Comparable接口。否则会抛出
                     ClassCastException异常。
                     当修改了对象的属性后, 
TreeSet不会重新排序。最适合TreeSet排序的是不可变类
(它们的对象的属性不能修改)。
                  b. 客户化排序                
                     除了自然排序外, TreeSet还支持客户化排序。java.util.Comparator接口提供了具体的排序方法, 它有一个
                     compare(Object x, Object y)方法,用于比较两个对象的大小, 当compare(x,y):
                     返回0,       表明   x和y相等
                     返回值大于0, 表明   x>y
                     返回值小于0, 表明   x<y

                   如果希望TreeSet按照Customer对象的name属性进行降序排列,
                   可以先创建一个实现Comparator接口的类  
                     CustomerComparator, 参见:
                     import java.util.*;
                     public class CustomerComparator implements Comparator {
                            public int compare(Object o1, Object o2) {
                                   Customer c1 = (Customer)o1;
                                   Customer c2 = (Customer)o2;
                                   if(c1.getName().compareTo(c2.getName())>0) return -1;
                                   if(c1.getName().compareTo(c2.getName())<0) return 1;
  return 0;
                            }

                            public static void main(String[] args) {
                                   Set set = new TreeSet(new CustomerComparator());
                                   Customer customer1 = new Customer("Tom",15);
                                   Customer customer3 = new Customer("Jack",16);
                                   Customer customer2 = new Customer("Mike",26);
                                   set.add(customer1);
                                   set.add(customer2);
                                   set.add(customer3);


                                   Iterator it = set.iterator();
                      
                                   while(it.hasNext()) {
                                         Customer customer = it.next();
                                         System.out.println(customer.getName() + " " + customer.getAge());
                                   }
                             }
                      }
                      打印输出:
                      Tom 15
                      Mike 26
                      Jack 16

            3. List
 
               主要特征是其元素以线性方式存储,集合中允许存放重复对象。主要实现类包括:

               . ArrayList: 代表长度可变的数组。允许对元素进行快速的随机访问,但是向ArrayList中插入与删除元素的速度较慢;
               . LinkedList: 在实现中采用链表结构。对顺序访问进行了优化,向List中插入和删除元素的速度较快,随机访问速度则相对较慢。
Vector:   是线程安全的集合
               遍历方式:
               a. list.get(i);    //通过索引检索对象;
               b. Iterator it = list.iterator();
                  it.next();

            4. Map    Key  Value
      HashMap  是线程不安全的集合。
      HashTable是线程安全的集合。
      TreeMap可以进行排序(对key进行排序)

               Map是一种把键对象和值对象进行映射的集合,它的每一个元素都包含一对键对象和值对象。向Map集合中加入元素时,
               必须提供一对键对象和值对象,从Map集合中检索元素时,只要给出键对象,就会返回对应的值对象。
               map.put("2", "Tuesday");
               map.put("3", "Wednsday");
               map.put("4", "Thursday");
               String day = map.get("2");    //day的值为"Tuesday"
               Map集合中的键对象不允许重复,如以相同的键对象加入多个值对象,第一次加入的值对象将被覆盖。
               对于值对象则没有唯一性的要求,可以将任意多个键对象映射到同一个值对象上。
               map.put("1", "Mon");
               map.put("1", "Monday");      //"1"此时对应"Monday"
               map.put("one", "Monday");    //"one"此时对应"Monday"
               Map有两种比较常见的实现:
               1) HashMap
                  按哈希算法来存取键对象,有很好的存取性能,为了保证HashMap能正常工作,和HashSet一样,中 要求当两个键对象通过equals()方法比较为true时,这两个键对象的hashCode()方法返回的哈希码也一样。
               2) TreeMap
                  实现了SortedMap接口,能对键对象进行排序。和TreeSet一样,TreeMap也支持自然排序和客户化排序两种方式。(排序按照的是KEY值)
                  Map map = new TreeMap();
                  map.put("1", "Monday");
                  map.put("3", "Wednsday");
                  map.put("4", "Thursday");
                  map.put("2", "Tuesday");
                  Set keys = map.keySet();
                  Iterator it = keys.iterator();
                  while(it.hasNext()) {
                        String key = (String)it.next();
                        String value= (String)map.get(key);
                        System.out.println(key + " " + value);
                  }
                  打印输出:
                  1 Monday
                  2 Tuesday
                  3 Wednsday
                  4 Thursday
四、主要实现类区别小结
Vector和ArrayList
1,vector是线程同步的,所以它也是线程安全的,而arraylist是线程异步的,是不安全的。如果不考虑到线程的安全因素,一般用arraylist效率比较高。
2,如果集合中的元素的数目大于目前集合数组的长度时,vector增长率为目前数组长度的100%,而arraylist增长率为目前数组长度的50%。如果在集合中使用数据量比较大的数据,用vector有一定的优势。
3,如果查找一个指定位置的数据,vector和arraylist使用的时间是相同的,如果频繁的访问数据,这个时候使用vector和arraylist都可以。而如果移动一个指定位置会导致后面的元素都发生移动,这个时候就应该考虑到使用linklist,因为它移动一个指定位置的数据时其它元素不移动。
ArrayList 和Vector是采用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,都允许直接序号索引元素,但是插入数据要涉及到数组元素移动等内存操作,所以索引数据快,插入数据慢,Vector由于使用了synchronized方法(线程安全)所以性能上比ArrayList要差,LinkedList使用双向链表实现存储,按序号索引数据需要进行向前或向后遍历,但是插入数据时只需要记录本项的前后项即可,所以插入数度较快。

arraylist和linkedlist
1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。 这一点要看实际情况的。若只对单条数据插入或删除,ArrayList的速度反而优于LinkedList。但若是批量随机的插入删除数据,LinkedList的速度大大优于ArrayList. 因为ArrayList每插入一条数据,要移动插入点及之后的所有数据。

HashMap与TreeMap
1、 HashMap通过hashcode对其内容进行快速查找,而TreeMap中所有的元素都保持着某种固定的顺序,如果你需要得到一个有序的结果你就应该使用TreeMap(HashMap中元素的排列顺序是不固定的)。
2、在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。

ArrayList和LinkedList
ArrayList和LinkedList在用法上没有区别,但是在功能上还是有区别的。LinkedList经常用在增删操作较多而查询操作很少的情况下,ArrayList则相反。

 五、Map集合

实现类:HashMap、Hashtable、LinkedHashMap和TreeMap

HashMap 

HashMap是最常用的Map,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度,遍历时,取得数据的顺序是完全随机的。因为键对象不可以重复,所以HashMap最多只允许一条记录的键为Null,允许多条记录的值为Null,是非同步的

Hashtable

Hashtable与HashMap类似,是HashMap的线程安全版,它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtale在写入时会比较慢,它继承自Dictionary类,不同的是它不允许记录的键或者值为null,同时效率较低。

ConcurrentHashMap

线程安全,并且锁分离。ConcurrentHashMap内部使用段(Segment)来表示这些不同的部分,每个段其实就是一个小的hash table,它们有自己的锁。只要多个修改操作发生在不同的段上,它们就可以并发进行。

LinkedHashMap

LinkedHashMap保存了记录的插入顺序,在用Iteraor遍历LinkedHashMap时,先得到的记录肯定是先插入的,在遍历的时候会比HashMap慢,有HashMap的全部特性。

TreeMap

TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序(自然顺序),也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。不允许key值为空,非同步的;

我的手机 2018/4/22 11:32:58

map的遍历

第一种:KeySet()
将Map中所有的键存入到set集合中。因为set具备迭代器。所有可以迭代方式取出所有的键,再根据get方法。获取每一个键对应的值。 keySet():迭代后只能通过get()取key 。
取到的结果会乱序,是因为取得数据行主键的时候,使用了HashMap.keySet()方法,而这个方法返回的Set结果,里面的数据是乱序排放的。
典型用法如下:
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<Map.Entry<K,V>> 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集合
Iterator it = map.entrySet().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中,所以快了。两种遍历的遍历时间相差还是很明显的。 

六、测试实例
    1、Collection类
public class CollectionTest1 {
	
	public static void main(String[] args) {
		CollectionTest1 ct=new CollectionTest1();
		//Collection c=new ArrayList<>();
		/*List c=new ArrayList<>();
		c.add("tom1");
		c.add(1);
		c.add(2.0);*/
		
		/*Set set=new HashSet<>();
		set.add("tom1");
		set.add(1);
		set.add(2.0);*/
		
		/*Map map=new HashMap<>();
		map.put("1", "tom1");
		map.put(2, "tom2");
		map.put("3", "tom3");
		ct.print(map);*/
		int[] array={1,22,1,4,12};
		ct.print(array);
	}
	public void print(Collection c){
		System.out.println("----collection");
		Iterator it = c.iterator();
		while (it.hasNext()) {
			Object o = it.next();
			System.out.println(o);
		}
		//增强for循环 jdk1.5
		//Object代表  遍历的数据类型
		//o 代表 遍历出来的每一个元素
		//c 代表目标资源 需要遍历的数据
		//增强for循环的底层 是使用迭代器遍历的
		/*for(Object o : c){
			System.out.println(o);
		}*/
	}
	public void print(List list){
		System.out.println("----list");
		/*Iterator it = list.iterator();
		while (it.hasNext()) {
			Object o = it.next();
			System.out.println(o);
		}*/
		/*for(int i=0;i<list.size();i++){
			System.out.println(list.get(i));
		}*/
		/*for(Object o : list){
			System.out.println(o);
		}*/
	}
	public void print(Set set){
		/*Iterator it = set.iterator();
		while (it.hasNext()) {
			Object value = it.next();
			System.out.println(value);
		}*/
		
		//set不能使用下标遍历
		/*for(int i=0;i<set.size();i++){
			System.out.println(set.get(i));
		}*/
		/*for(Object o:set){
			System.out.println(o);
		}*/
	}
	public void print(Map map){
		// keySet() entrySet() values()
		//Set set = map.entrySet();
		//Entry  key value 
		/*for(Object o:set){
			Entry entry=(Entry) o;
			Object key = entry.getKey();
			Object value = entry.getValue();
			System.out.println("key:"+key+"---value:"+value);
		}*/
		
	/*	Iterator it = set.iterator();
		while (it.hasNext()) {
			Object o = it.next();
			Entry entry=(Entry) o;
			Object key = entry.getKey();
			Object value = entry.getValue();
			System.out.println("key:"+key+"---value:"+value);
		}*/
		//所有的key
	/*	Set keySet = map.keySet();
		for(Object key:keySet){
			System.out.print("key:"+key);
			System.out.println("---value:"+map.get(key));
		}*/
		
		Collection c = map.values();
		//打印map集合中的所有value值
		for(Object value:c){
			System.out.println(value);
		}
	}
	 public void print(int[] array){
		 for(int arr:array){
			 System.out.println(arr);
		 }
	 }
	 public void print(String[] array){
		 for(String arr:array){
			 System.out.println(arr);
		 }
	 }
	
}


    2、Map和Set

public class MapTest {
	public static void main(String[] args) {
		//map集合存储的key不能相同,value值可以相同
		//如果key值相同 那么以最后一次覆盖的值为准
		Map map=new HashMap();
		
		//key---value
		map.put(1, "tom");  //Entry<K, V>
		map.put("name", "zhangsan"); //Entry<K, V>
		map.put(2, "lisi");//Entry<K, V>
		map.put(3, "lisi");//Entry<K, V>
		
		//Entry对象的set集合  Entry里边放的是key 和 value
		/*Set entrySet = map.entrySet();
		
		Iterator it = entrySet.iterator();
		
		while (it.hasNext()) {
			//Entry
			Object o = it.next();
			//每对map集合的数据 key value
			Map.Entry entry=(Map.Entry)o;
			
			Object key = entry.getKey();
			Object value = entry.getValue();
			
	        System.out.println("key:"+key+"--"+"value:"+value);
		}*/
		
		//所有key以set集合的形式返回
		/*Set keySet = map.keySet();
		Iterator it1 = keySet.iterator();
		while (it1.hasNext()) {
			//key
			Object key = it1.next();
			
			Object value = map.get(key);
			
		    System.out.println("key:"+key+"--"+"value:"+value);
		}*/
		//map集合所有的value值
		/*Collection c = map.values();
		Iterator it2 = c.iterator();
		while (it2.hasNext()) {
			//value
			Object value = it2.next();
			System.out.println("value:"+value);
		}*/
		
	/*	map.remove("name");
		
		map.clear();*/
		
		//map.equals(map1);
		
		//map.hashCode();
	 /*   boolean res=map.containsKey(1);
		
		boolean res1=map.containsValue("tom");
		
		boolean empty = map.isEmpty();
		
		int num=map.size();*/
		
		//map集合是没有下标的 这样遍历不行s
		/*for(int i=0;i<map.size();i++){
			//i=0
			map.get(i);
		}*/
		/*Map map1=new HashMap<>();
		map1.put("map1key1", "1");
		map1.put("map1key2", "2");
		map1.put("map1key3", "3");
		map1.put("map1key4", "4");
		
		map.putAll(map1);*/
		
		Set keySet = map.keySet();
		Iterator it1 = keySet.iterator();
		while (it1.hasNext()) {
			//key
			Object key = it1.next();
			
			Object value = map.get(key);
			
		    System.out.println("key:"+key+"--"+"value:"+value);
		}
		
	}

}

3.HashCode

    

public class HashcodeTest {
	
	public static void main(String[] args) {
		Student s1=new Student(20,"tom");
		Student s2=new Student(20,"tom");
		//hashcode哈希码不同的两个对象,一定是不同的对象
		//hashcode相同的两个对象,不一定是同一个对象
		System.out.println(s1.hashCode());
		System.out.println(s2.hashCode());
		Set s=new HashSet<>();
		s.add(s1);
		s.add(s2);
		
		Iterator it=s.iterator();
		while(it.hasNext()){
			Object next = it.next();
			System.out.println(next);
		}
		
		/*String ss1=new String("tom");
		String ss2=new String("tom");
		
		Set set=new HashSet();
		set.add(ss1);
		set.add(ss2);
		
		Iterator it=set.iterator();
		while(it.hasNext()){
			Object next = it.next();
			System.out.println(next);
		}*/
		
	}

}








猜你喜欢

转载自blog.csdn.net/fxy_cdns/article/details/80037190