集合(Collection)
1. 什么是集合?
集合是一组对象组成的一个整体,又称为容器,集合类属于java.util包。集合不同于数组的地方在于,一是它的容量是可变的,二是集合中只能存储对象,不能存储基本数据类型
Java的集合类库有一大一小两派,一大由Collection接口带领,一小由Map接口带领。
2. Collection接口
Collection接口下有两大分支,分别为List接口和Set接口。注意,Collection是接口,而Collections则是java.util包下的一个具体类。
2.1 List接口
List集合中允许有重复对象。
2.1.1 ArrayList类
ArrayList分配了一个动态再分配的对象数组数组(说白了就是长度可变的数组),可以通过索引进行随机访问,但是插入删除比较麻烦,这与数组的特性是一致的。
List list = new ArrayList();
注:ArrayList是线程不安全的,即它的方法没有同步;而Vector是线程安全的,它的所有方法都是同步的。
2.1.2 LinkedList类
LinkedList采用链表结构保存对象,插入删除方便,但是访问慢,这和链表的特性是一致的。
List list2 = new LinkedList();
1 import java.util.ArrayList; 2 import java.util.List; 3 4 public class Gather{ 5 public static void main(String[] args) { 6 List list = new ArrayList<>(); 7 for(char ch = 'a'; ch <= 'z'; ch++){ 8 list.add(ch); 9 } 10 11 int ran = (int)(Math.random()*(list.size()-1)); 12 System.out.println("随机获取list中的元素:"+list.get(ran)); 13 14 list.remove(2); 15 for(int i = 0; i < list.size(); i++){ 16 System.out.print(list.get(i)+" "); 17 } 18 } 19 }
2.2 Set接口
Set是没有重复元素的集合,遍历Set集合的顺序和插入顺序并不总是一致,Set集合中的元素可以看作是无序的。
2.2.1 HashSet类
HashSet基于散列表
举例:
1 public class People { 2 private String name; 3 private long id_card; 4 public People(String name, long id_card){ 5 this.name = name; 6 this.id_card = id_card; 7 } 8 9 public long getId_card(){ 10 return id_card; 11 } 12 13 public void setId_card(long id_card){ 14 this.id_card = id_card; 15 } 16 17 public String getName(){ 18 return name; 19 } 20 21 public void setName(String name){ 22 this.name = name; 23 } 24 } 25 ——————————华丽的分割线———————————— 26 import java.util.HashSet; 27 import java.util.Iterator; 28 import java.util.Set; 29 30 public class CollectionDemo { 31 32 public static void main(String[] args) { 33 Set<People> hashSet = new HashSet<People >(); 34 hashSet.add(new People("张三", 2000)); 35 hashSet.add(new People("李四", 2001)); 36 hashSet.add(new People("王五", 2002)); 37 38 Iterator<People> it = hashSet.iterator(); 39 System.out.println("集合中的元素是:"); 40 while(it.hasNext()){ 41 People person = it.next(); 42 System.out.println(person.getName()+" "+person.getId_card()); 43 } 44 } 45 }
2.2.2 TreeSet类
TreeSet树集是一个有序集合,可以以任意顺序将元素插入到集合中。遍历时,按照排序后的顺序呈现,排序是用红黑树完成的。
TreeSet如何知道元素的大小呢,在默认情况下,TreeSet假定插入的元素实现了Comparable接口。这个接口定义了一个方法:
Public interface Comparable<T>{
Int compareTo(T other);
}
通过Comparable接口定义排列顺序有局限性。对于一个给定的类,只能实现这个接口一次,如果想要使用不同规则,多次对类对象进行排序,就要用到Comparator对象传递给TreeSet构造器来定义排序规则。Comparator接口声明了一个带有两个显式参数的compare方法:
Public interface Comparator<T>{
Int compare(T a, T b);
}
与compareTo方法一样,如果a位于b之前compare方法则返回负值,相等返回0,否则返回正值。
如果按照描述信息进行排序,就直接定义一个实现Comparator接口的类:
Class ItemComparator implements Comparator<Item>{
Public int compare(Item a, Item b){
String descrA = a.getDescription();
String descrB = b.getDescription();
Return descrA.compareTo(descrB);
}
}
然后将这个类的对象传递给树集的构造器:
ItemComparator comp = new ItemComparator();
SortedSet<Item> sortByDescrition = new TreeSet<>(comp);
(总是就是,在构造TreeSet时,传给它一个对象参数,这个对象是实现了Comparator接口的类的一个实例,它定义了一种比较TreeSet集中元素的规则,就酱紫(⊙o⊙)哦)
注:在Java中,散列表用链表数组实现,每个链表被称为桶(bucket)。要想查找表中对象的位置,就要先计算它的散列码(也就是哈希码hashCode),然后与桶的总数取余,所得到的结果就是保存这个元素的桶的索引。例如,散列码76268,并且有128个桶,则对象应该保存在第108号桶中。
桶数是指用于收集具有相同散列值的桶的数目。标准类库使用的桶数是2的幂,默认值为16(为表大小提供的任何值都将被自动转换为2的下一个幂)。
散列表可以用于实现几个重要的数据结构,其中最简单的就是set类型。Set的add方法首先在集中查找要添加的对象,如果不存在,就将这个对象添加进去。
3.Map接口
Map提供了将键值映射到值的对象,键不能重复,一个键映射到一个值。
3.1 HashMap类
HashMap也叫散列映射表,它对键进行散列,散列或比较函数只能作用于键,与键关联的值不能进行散列或比较。
HashMap是基于哈希表的Map接口的实现,增删的效率更高。允许null值和null键。
3.2 TreeMap类
TreeMap也叫树映射表,用键的整体顺序对元素进行排序。
TreeMap类不仅实现了Map类,还实现了java.util.SortedMap接口,因此集合中元素具有一定的顺序。增删效率比HashMap差。由于映射关系是根据键对象按照一定的顺序排列的,所以不允许键值为null
1 public class Emp { 2 private String e_id; 3 private String e_name; 4 public Emp(String e_id,String e_name){ 5 this.e_id = e_id; 6 this.e_name = e_name; 7 } 8 public String getE_id() { 9 return e_id; 10 } 11 public void setE_id(String e_id) { 12 this.e_id = e_id; 13 } 14 public String getE_name() { 15 return e_name; 16 } 17 public void setE_name(String e_name) { 18 this.e_name = e_name; 19 } 20 } 21 —————————华丽的分割线——————————— 22 import java.util.HashMap; 23 import java.util.Iterator; 24 import java.util.Map; 25 import java.util.Set; 26 import java.util.TreeMap; 27 28 public class MapText { 29 public static void main(String[] args) { 30 Map map = new HashMap(); 31 Emp emp = new Emp("001","zhangsan"); 32 Emp emp2 = new Emp("015","lisi"); 33 Emp emp3 = new Emp("009","wangyi"); 34 Emp emp4 = new Emp("011","tangbao"); 35 map.put(emp.getE_id(), emp.getE_name()); 36 map.put(emp2.getE_id(),emp2.getE_name()); 37 map.put(emp3.getE_id(), emp3.getE_name()); 38 map.put(emp4.getE_id(), emp4.getE_name()); 39 40 Set set = map.keySet(); 41 Iterator it = set.iterator(); 42 //HashMap的无序是指既不是按照添加顺序,也不是按照键值大小顺序 43 /*HashMap中哈希码的定义 44 * public final int hashCode() { 45 return Objects.hashCode(key) ^ Objects.hashCode(value); 46 } 47 *Objects类中哈希码的定义(Objects继承自Object) 48 *public static int hashCode(Object o) { 49 return o != null ? o.hashCode() : 0; 50 } 51 */ 52 System.out.println("HashMap类实现的Map集合,无序:"); 53 while(it.hasNext()){ 54 String str = (String)it.next(); 55 String name = (String)map.get(str); 56 System.out.println(str+":"+name); 57 } 58 59 TreeMap treemap = new TreeMap(); 60 treemap.putAll(map); 61 Iterator it2 = treemap.keySet().iterator(); 62 System.out.println("TreeMap 类实现的Map集合,键对象升序:"); 63 while(it2.hasNext()){ 64 String str = (String)it2.next(); 65 String name = (String)map.get(str); 66 System.out.println(str+""+name); 67 } 68 } 69 }
3.3其它
集合框架并没有将映射表视为一个集合,但是可以获得映射表的视图,这时一组实现了Collection接口的对象,或者它的子接口的视图。
有3个视图,分别是:键集、值集合(不是集)和键/值对集。键与键/值对形成了一个集,这是因为在映射表中一个键只能有一个副本。
Set<K> keySet()
Collection<K> values()
Set<Map.Entry<K,V>> entrySet()
注意:keySet既不是HashSet,也不是TreeSet,而是实现了Set接口的某个其他类的对象。Set接口扩展了Collection接口。
4.Iterator接口
通过迭代器Iterator接口可以遍历集合。
Iterator接口位于java.util包下,它有3个方法:
hasNext(): 如果还有元素可迭代,返回true
next(): 返回迭代的下一个元素
remove(): 删除上次调用next()方法时返回的元素(注意:对next()和remove()方法的调用具有相互依赖性,调用remove()之前没有调用next()是不合法的。如果想要删除后面的元素,必须通过调用next()越过前面的元素才行)
反复调用next()方法可以遍历集合,但是当到达了集合末尾,会抛出NoSuchElementException。
1 Collection<String> c = ...; 2 Iterator<String> iter = c.iterator(); 3 while(iter.hasNext()){ 4 String element = iter.next(); 5 Do sth with element 6 } 7 //从Java SE 5.0 起,可以用for each循环来遍历 8 for(String element : c){ 9 Do sth with element 10 }
4.1迭代顺序
元素被访问的顺序取决于集合类型,如果对ArrayList进行迭代,从索引为0的位置开始,没迭代一次,索引加1。如果访问的是HashSet,元素将按随机顺序出现,虽然可以保证遍历,但无法预知访问次序。
4.2 ListIterator接口
1 import java.util.ArrayList; 2 import java.util.Collections; 3 import java.util.Iterator; 4 import java.util.ListIterator; 5 6 public class ListIteratorDemo { 7 public static void main(String[] args) { 8 ArrayList<Integer> array = new ArrayList<>(); 9 Collections.addAll(array, 1,2,3,4,5,6); 10 System.out.println("集合中的元素:"+array); 11 ListIterator<Integer> iterator = array.listIterator(); 12 13 boolean hasNext = iterator.hasNext(); 14 System.out.println("集合是否还有下一个元素:"+hasNext); 15 16 boolean hasPrevious = iterator.hasPrevious(); 17 System.out.println("集合是否还有前一个元素:"+hasPrevious); 18 System.out.println("nextIndex:"+iterator.nextIndex()); 19 System.out.println("next:"+iterator.next()); 20 System.out.println("nextIndex:"+iterator.nextIndex()); 21 System.out.println("next:"+iterator.next()); 22 System.out.println("previousIndex:"+iterator.previousIndex()); 23 System.out.println("previous:"+iterator.previous()); 24 25 System.out.println("nextIndex:"+iterator.nextIndex()); 26 System.out.println("next:"+iterator.next()); 27 28 //add将元素插入到nextIndex所指的位置 29 System.out.println("nextIndex:"+iterator.nextIndex()); 30 iterator.add(999); 31 System.out.println("向集合中添加999后:"+array); 32 33 //set将next()返回的元素替换为100 34 iterator.next(); 35 iterator.set(100); 36 System.out.println("集合中的元素:"+array); 37 38 //remove将100删除 39 iterator.remove(); 40 System.out.println("集合中的元素:"+array); 41 } 42 }
5.Deque接口
Java SE 6中引入了Deque接口,并由ArrayDeque和LinkedList类实现。这两个类都提供了双端队列。在必要时可增加队列长度。
public interface Deque<E> extends Queue<E>
6.集合框架
框架(framework)是一个类的集,它奠定了创建高级功能的基础。框架包含很多超类,这些超类拥有非常有用的功能、策略和机制。框架使用者创建的子类可以扩展超类的功能,而不必重新创建这些基本的机制。例如,Swing就是一种用户界面的机制。