Collection与Map容器
1.Collection接口
Collection 是java集合框架的顶层接口,它表示为容器,只能存储应用类型数据,为了方便后续遍历最后存储同一类型数据。
Collection 有增删改查等方法。
1 public static void main(String[] args) { 2 3 /** 4 * 增:add/addAll 5 * 删:clear/remove/removeAll/retainAll 6 * 改: 7 * 查:contains/containsAll/isEmpty/size 8 */ 9 10 Collection c1 = new ArrayList(); 11 12 // 追加 13 c1.add("apple"); // Object object = new String("apple"); 14 // c1.add(1); // Object object = new Integer(1); 15 c1.add("banana"); 16 System.out.println(c1); 17 18 // 追加一个集合 19 Collection c2 = new ArrayList(); 20 c2.add("java"); 21 c2.add("c++"); 22 c1.addAll(c2); 23 System.out.println(c1); 24 25 // clear 26 //c1.clear(); 27 28 // c1.remove("apple"); 29 // c1.removeAll(c2); 30 //c1.retainAll(c2); 31 //System.out.println(c1); 32 33 System.out.println(c1.contains("apple")); 34 c2.add("js"); 35 System.out.println(c1.containsAll(c2)); 36 // c1.clear(); 37 System.out.println(c1.isEmpty()); 38 // 返回集合元素的个数 39 System.out.println(c1.size()); 40 41 System.out.println(c1.equals(c2)); 42 43 }
集合的遍历
Iterable 可遍历的接口,集合接口继承于它,集合支持快速遍历。
1 // 快速遍历 2 // for-each 3 // Object 表示元素类型 4 // item表示迭代变量 5 // c1表示集合 6 for (Object item : c1) { 7 System.out.println(item.toString()); 8 }
快速遍历的本质
Collection继承Iterable接口,表示集合支持快速遍历。Iterable接口定义了一个方法iterator()用于获取集合的迭代器,是一个Iterator接口类型,iterator()内部返回一个实现类Iterator接口。这个实现类一定具有hasNext和next方法用于判断是否有下一个元素和获取下一个元素。快速遍历就是基于迭代器工作的。
1 public static void main(String[] args) { 2 3 4 Collection c1 = new ArrayList(); 5 c1.add("apple"); 6 c1.add("banana"); 7 c1.add("coco"); 8 9 10 // 快速遍历 11 // for-each 12 // Object 表示元素类型 13 // item表示迭代变量 14 // c1表示集合 15 for (Object item : c1) { 16 System.out.println(item.toString()); 17 } 18 19 // 迭代器遍历(国内) 20 Iterator it = c1.iterator(); 21 while(it.hasNext()) { 22 Object item = it.next(); 23 System.out.println(item.toString()); 24 } 25 26 // 国外 27 for(Iterator it2=c1.iterator();it2.hasNext();) { 28 Object item = it2.next(); 29 System.out.println(item.toString()); 30 } 31 }
Collection 有两个子接口分别为Set 和 List
Set 接口是无序的、唯一的;List接口是有序的、不唯一的。
List 接口
1.1.1 List常用方法
1 public static void main(String[] args) { 2 3 /** 4 * 增:add/addAll/add(index,el)/addAll(index,collection) 5 * 删:clear/remove/removeAll/remove(index) 6 * 改:set(index,el) 7 * 查:get(index)/indexOf/lastIndexOf() 8 * 其他:contains/containsAll/isEmpty/size 9 */ 10 List list1 = new ArrayList(); 11 // 添加元素 12 list1.add("apple"); 13 list1.add("banana"); 14 // 在指定位置添加元素 15 list1.add(0, "coco"); 16 17 System.out.println(list1); 18 19 List list2 = new ArrayList(); 20 list2.add("java"); 21 list2.add("c++"); 22 23 list1.addAll(1, list2); 24 System.out.println(list1); 25 26 // 删除 27 list1.remove(0); 28 System.out.println(list1); 29 30 // 修改 31 list1.set(0, "javax"); 32 System.out.println(list1); 33 34 // 查 35 System.out.println(list1.get(0)); 36 list1.add("apple"); 37 list1.add("apple"); 38 System.out.println(list1); 39 System.out.println(list1.indexOf("apple")); 40 System.out.println(list1.lastIndexOf("apple")); 41 }
1.1.1 List接口遍历
ListIterator 继承于Iterator,在Iterator的基础上提供了以正向遍历集合,也可以以逆序遍历集合。
hasNext/next 以正向遍历
hasPrevious/previous 以逆序遍历
1 public static void main(String[] args) { 2 3 4 List list1 = new ArrayList(); 5 list1.add("apple"); 6 list1.add("banana"); 7 list1.add("coco"); 8 9 // 【1】快速遍历 10 System.out.println("--for each--"); 11 for (Object item : list1) { 12 System.out.println(item.toString()); 13 } 14 15 // 【2】普通for 16 System.out.println("--for--"); 17 for(int i=0;i<list1.size();i++) { 18 System.out.println(list1.get(i)); 19 } 20 21 // 【3】集合迭代器 22 System.out.println("--iterator--"); 23 Iterator it = list1.iterator(); 24 while(it.hasNext()) { 25 System.out.println(it.next()); 26 } 27 28 System.out.println("--list iterator--"); 29 // 正向遍历 30 ListIterator it2 = list1.listIterator(); 31 while(it2.hasNext()) { 32 System.out.println(it2.next()); 33 } 34 35 // 逆序遍历 36 while(it2.hasPrevious()) { 37 System.out.println(it2.previous()); 38 } 39 40 System.out.println("--list iterator with index--"); 41 ListIterator it3 = list1.listIterator(1); 42 while(it3.hasNext()) { 43 System.out.println(it3.next()); 44 } 45 }
因Link 接口继承于Collection接口,所以拥有增删改查方法且自身拥有一些特殊方法。
Link接口有三个重要的实现类:ArrayLink、vector、LinkedList
ArrayLink
ArrayLink的底层数据结构是数组,
默认数组大小是10,如果添加的元素个数超过默认容量,ArrayList会自动拓容,拓容原则:newCapacity = oldCapacity + oldCapacity / 2;
如果未来确定序列的元素不在增加,通过调用trimToSize()调制容量至合适的空间。
ArrayList作为List接口的实现类,常用方法和遍历方法参考List接口。
优点:使用索引查询效率高,添加有序。缺点:删除、增加数据效率低
Vector
Vector 的底层数据结构也是数组,默认数组大小是10,如果添加的元素个数超过默认容量,Vector会自动拓容,拓容原则:newCapacity = oldCapacity +capacityIncrement(增长因子);如果未来确定序列的元素不在增加,通过调用trimToSize()调制容量至合适的空间。
注意:Vector 在实现List接口的同时,同添加了自身特有的方法xxxElement,未来使用时为了程序的可拓展性,一定要按照接口来操作Vector。
Vector跟ArrayLink基本一样;区别在于Vector是线程安全的,ArrayLink线程不安全。
LinkedList
LinkedList的底层数据结构是链表,优点:增删效率高,添加有序;缺点:查询效率低,线程是不安全的。
除了实现List接口, 还实现栈接口
push入栈操作 / pop出栈操作
1 public class Test01 { 2 public static void main(String[] args) { 3 LinkedList list = new LinkedList(); 4 list.push("apple"); 5 list.push("banana"); 6 list.push("coco"); 7 8 9 System.out.println(list.pop()); 10 System.out.println(list.pop()); 11 System.out.println(list.pop()); 12 13 // java.util.NoSuchElementException 14 System.out.println(list.pop()); 15 } 16 }
offer/poll/peek 可能会返回特殊值(null)
1 public static void main(String[] args) { 2 3 LinkedList queue = new LinkedList(); 4 // 入队 5 /** 6 * 队列头 队列尾 7 *<----- <----- 8 * [apple, banana, coco] 9 */ 10 queue.offer("apple"); 11 queue.offer("banana"); 12 queue.offer("coco"); 13 14 // 出队列 15 //System.out.println(queue.poll()); 16 //System.out.println(queue.poll()); 17 //System.out.println(queue.poll()); 18 System.out.println(queue); 19 20 //System.out.println(queue.poll()); 21 22 // 获取表头元素 23 System.out.println(queue.peek()); 24 25 }
Set接口
因Set 接口继承于Collection接口,所以拥有增删改查方法且自身拥有一些特殊方法
Set接口的三个实现类分别是:HarhSet、LinkedHarhSet、TreeSet
HarhSet
HarhSet的底层数据结构是哈希表,优点:增删查效率高;缺点:无序
LinkedHarhSet
LinkedHarhSet底层数据结构是链表+哈希表;优点:增删改效率高,添加有序;
TreeSet
TreeSet 底层数据结构是二叉树;唯一的,增删查效率中等,存储的数据按照一定的规则存储;存储规则让数据表现出自然顺序。
2.泛型(generic)
概念:
泛型允许开发者在强类型程序设计语言(java)编写代码时定义一些可变部分,这些部分在使用前必须作出指明。
泛型就是将类型参数化
ArrayList<E> list表示声明了一个列表list,列表的元素是E类型
ArrayList<String> list = new ArrayList<String>();
声明了一个列表list,列表的元素只能是String类型。
泛型在编译器起作用,运行时jvm察觉不到泛型的存在。
泛型的擦除
泛型在运行时已经被擦除了。
1 public static void main(String[] args) { 2 ArrayList<String> list = new ArrayList<String>(); 3 list.add("apple"); 4 System.out.println(list instanceof ArrayList); 5 System.out.println(list instanceof ArrayList<String>); 6 } 7 Cannot perform instanceof check against parameterized type ArrayList<String>. Use the form ArrayList<?> instead since further generic type information will be erased at runtime
泛型的应用
泛型类
当一个类中属性的数据类型不确定时,具体是什么类型由使用者来确定时,使用泛型。泛型类的形式
定义一个泛型类
1 public class FanClass<T> { 2 private T t; 3 4 public T getT() { 5 return t; 6 } 7 8 public void setT(T t) { 9 this.t = t; 10 } 11 12 public FanClass(T t) { 13 super(); 14 this.t = t; 15 } 16 17 public FanClass() { 18 super(); 19 } 20 } 21 22 public class Test01 { 23 public static void main(String[] args) { 24 FanClass<String> fan = new FanClass<String>(); 25 fan.setT("apple"); 26 27 FanClass<Integer> fan2 = new FanClass<Integer>(); 28 fan2.setT(1); 29 } 30 }
泛型方法
当一个方法的参数类型不确定时,具体是什么类型由使用者来确定,可以考虑使用泛型方法。形式:
1 public class Student { 2 3 4 /*public void showInfo(int a) { 5 System.out.println(a); 6 } 7 8 public void showInfo(float a) { 9 System.out.println(a); 10 } 11 12 public void showInfo(String a) { 13 System.out.println(a); 14 }*/ 15 16 public <T> void showInfo(T a) { 17 System.out.println(a); 18 } 19 } 20 21 public static void main(String[] args) { 22 23 Student stu = new Student(); 24 stu.showInfo(1); 25 stu.showInfo("apple"); 26 stu.showInfo(1.0f); 27 }
泛型方法在调用时确定(指明)类型。
泛型方法在一定程度上优化了方法重载。
泛型方法可以定义多个泛型类型
1 // 可以定义多个泛型的类型 2 public <A,B> void showInfo(A a,B b) { 3 System.out.println(a); 4 System.out.println(b); 5 }
多个泛型类型进一步优化了方法重载。
多个同类型的泛型
1 // 多个同类型的泛型 2 /*public <A> void print(A a) { 3 System.out.println(a); 4 } 5 public <A> void print(A a,A b) { 6 System.out.println(a); 7 System.out.println(b); 8 }*/ 9 10 public <A> void print(A...a) { 11 System.out.println(a); 12 }
A… a 表示方法可以接受多个参数。当调用方法传递多个参数时,多个参数被放到a数组中,a是什么类型的数组由开发者调用处传参决定。
1 stu.print(1); 2 stu.print(1,2); 3 4 stu.print("apple"); 5 stu.print("apple","banana");
print(A...a) 方法称为可变参数的泛型形式。
1.1.1 泛型接口(C)
如果接口中的方法的参数(形参、返回值)不确定时,可以考虑使用泛型接口。形式
1 public interface FanInterface<T> { 2 public void showInfo(T t); 3 }
[1]实现类能确定泛型接口的类型
1 public class ImplClass implements FanInterface<String>{ 2 3 @Override 4 public void showInfo(String t) { 5 // TODO Auto-generated method stub 6 7 } 8 }
[2]实现类不能确定泛型接口的类型->继续泛。
1 public class ImplClass2<T> implements FanInterface<T>{ 2 3 @Override 4 public void showInfo(T t) { 5 6 } 7 }
1.1.1.1 泛型的上限和下限 (C)
1 public static void print(ArrayList<? extends Pet> list) { 2 for (Pet pet : list) { 3 pet.showInfo(); 4 } 5 }
泛型的上限ArrayList(? extends Pet) list 声明了一个容器,容器中的元素类型一定要继承于Pet,我们称这种形式叫做泛型的上限。
泛型的下限ArrayList(? super Pet) list 声明了一个容器,容器中的元素类型一定要是Pet的父类,我们称这个形式为泛型的下限。
3.Map接口
Map接口称为键值对集合或者映射集合,其中的元素(entry)是以键值对(key-value)的形式存在。
Map 容器接口中提供了增、删、改、查的方式对集合进行操作。
Map接口中都是通过key来操作键值对,一般key是已知。通过key获取value。
Map接口的三个重要实现类:TreeMap \LinkHashMap \HashMap
TreeMap
TreeMap底层数据结构为二叉树;key以TreeSet(二叉树)存储。
1 public static void main(String[] args) { 2 3 4 /*TreeMap<String, Object> map = new TreeMap<String,Object>(new Comparator<String>() { 5 6 @Override 7 public int compare(String o1, String o2) { 8 return o1.length() - o2.length(); 9 } 10 }); 11 12 ArrayList<String> list2 = new ArrayList<String>(); 13 list2.add("ben"); 14 list2.add("bill"); 15 map.put("Aa", list2); 16 17 ArrayList<String> list1 = new ArrayList<String>(); 18 list1.add("alex"); 19 list1.add("alice"); 20 list1.add("allen"); 21 map.put("B", list1); 22 23 System.out.println(map);*/ 24 25 26 27 TreeMap<Student, Object> map = new TreeMap<Student,Object>(new Comparator<Student>() { 28 29 @Override 30 public int compare(Student o1, Student o2) { 31 return o1.getAge() - o2.getAge(); 32 } 33 }); 34 35 ArrayList<String> list1 = new ArrayList<String>(); 36 list1.add("alex"); 37 list1.add("alice"); 38 list1.add("allen"); 39 Student s1 = new Student("001", "大狗", 20); 40 map.put(s1, list1); 41 42 43 ArrayList<String> list2 = new ArrayList<String>(); 44 list2.add("ben"); 45 list2.add("bill"); 46 Student s2 = new Student("001", "2狗", 20); 47 // 修改 48 map.put(s2, list2); 49 System.out.println(map); 50 51 }
LinkHashMap
LinkHashMap底层数据结构为链表+哈希表;key以LinkedHashSet存储。哈希表散列key,链表维持key的添加顺序。
1 public static void main(String[] args) { 2 3 4 /*LinkedHashMap<String, Object> map = new LinkedHashMap<String,Object>(); 5 6 ArrayList<String> list2 = new ArrayList<String>(); 7 list2.add("ben"); 8 list2.add("bill"); 9 map.put("B", list2); 10 11 ArrayList<String> list1 = new ArrayList<String>(); 12 list1.add("alex"); 13 list1.add("alice"); 14 list1.add("allen"); 15 map.put("A", list1); 16 17 System.out.println(map);*/ 18 19 20 21 HashMap<Student, Object> map = new HashMap<Student,Object>(); 22 23 ArrayList<String> list1 = new ArrayList<String>(); 24 list1.add("alex"); 25 list1.add("alice"); 26 list1.add("allen"); 27 Student s1 = new Student("001", "大狗", 20); 28 map.put(s1, list1); 29 30 31 ArrayList<String> list2 = new ArrayList<String>(); 32 list2.add("ben"); 33 list2.add("bill"); 34 Student s2 = new Student("001", "大狗", 20); 35 // 修改 36 map.put(s2, list2); 37 System.out.println(map); 38 39 }
HashMap
HashMap底层数据结构哈希表;key以HashSet存储。
1 public static void main(String[] args) { 2 3 /* 4 HashMap<String, Object> map = new HashMap<String,Object>(); 5 6 ArrayList<String> list1 = new ArrayList<String>(); 7 list1.add("alex"); 8 list1.add("alice"); 9 list1.add("allen"); 10 map.put("A", list1); 11 12 13 ArrayList<String> list2 = new ArrayList<String>(); 14 list2.add("ben"); 15 list2.add("bill"); 16 map.put("B", list2); 17 18 System.out.println(map); 19 */ 20 21 22 HashMap<Student, Object> map = new HashMap<Student,Object>(); 23 24 ArrayList<String> list1 = new ArrayList<String>(); 25 list1.add("alex"); 26 list1.add("alice"); 27 list1.add("allen"); 28 Student s1 = new Student("001", "大狗", 20); 29 map.put(s1, list1); 30 31 32 ArrayList<String> list2 = new ArrayList<String>(); 33 list2.add("ben"); 34 list2.add("bill"); 35 Student s2 = new Student("001", "大狗", 20); 36 // 修改 37 map.put(s2, list2); 38 System.out.println(map); 39 40 }
总结:
[1] 向HashMap中存储元素时,key一定要实现hashCode和equals
[2] 一般建议使用String作为Map接口的key