传统的容器(数组)在进行增、删等破坏性操做时,须要移动元素,可能致使性能问题;同时添加、删除等算法和具体业务耦合在一块儿,增长了程序开发的复杂度。java
Java集合框架提供了一套性能优良、使用方便的接口和类,它们位于java.util包中c++
Collection是java集合框架(collection-frame)中的顶层接口。程序员
Collection接口表示一个容器,容器中只能存储引用数据类型,建议存同一类型的引用类型,方便后续遍历等操做。算法
容器中的元素能够是有序的、可重复的,称为List接口数组
也多是无序的、惟一的,称为Set接口。安全
public static void main(String[] args) {数据结构
/**并发 * 增:add/addAllapp * 删:clear/remove/removeAll/retainAll框架 * 改: * 查:contains/containsAll/isEmpty/size */
Collection c1 = new ArrayList();
// 追加 c1.add("apple"); // Object object = new String("apple"); // c1.add(1); // Object object = new Integer(1); c1.add("banana"); System.out.println(c1);
// 追加一个集合 Collection c2 = new ArrayList(); c2.add("java"); c2.add("c++"); c1.addAll(c2); System.out.println(c1);
// clear //c1.clear();
// c1.remove("apple"); // c1.removeAll(c2); //c1.retainAll(c2); //System.out.println(c1);
System.out.println(c1.contains("apple")); c2.add("js"); System.out.println(c1.containsAll(c2)); // c1.clear(); System.out.println(c1.isEmpty()); // 返回集合元素的个数 System.out.println(c1.size());
System.out.println(c1.equals(c2));
} |
Iterable 可遍历的接口,集合接口继承于它,集合支持快速遍历。
// 快速遍历 // for-each // Object 表示元素类型 // item表示迭代变量 // c1表示集合 for (Object item : c1) { System.out.println(item.toString()); } |
快速遍历的本质
Collection继承Iterable接口,表示集合支持快速遍历。Iterable接口定义了一个方法iterator()用于获取集合的迭代器,是一个Iterator接口类型,iterator()内部返回一个实现类实现类Iterator接口。这个实现类必定具备hasNext和next方法用于判断是否有下一个元素和获取下一个元素。快速遍历就是基于迭代器工做的。
public static void main(String[] args) {
Collection c1 = new ArrayList(); c1.add("apple"); c1.add("banana"); c1.add("coco");
// 快速遍历 // for-each // Object 表示元素类型 // item表示迭代变量 // c1表示集合 for (Object item : c1) { System.out.println(item.toString()); }
// 迭代器遍历(国内) Iterator it = c1.iterator(); while(it.hasNext()) { Object item = it.next(); System.out.println(item.toString()); }
// 国外 for(Iterator it2=c1.iterator();it2.hasNext();) { Object item = it2.next(); System.out.println(item.toString()); } } |
List 接口中的元素时有序的、可重复的。List接口中的元素经过索引(index)来肯定元素的顺序。
有序的 collection(也称为序列)。能够对列表中每一个元素的插入位置进行精确地控制。用户能够根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素
public static void main(String[] args) {
/** * 增:add/addAll/add(index,el)/addAll(index,collection) * 删:clear/remove/removeAll/remove(index) * 改:set(index,el) * 查:get(index)/indexOf/lastIndexOf() * 其余:contains/containsAll/isEmpty/size */ List list1 = new ArrayList(); // 添加元素 list1.add("apple"); list1.add("banana"); // 在指定位置添加元素 list1.add(0, "coco");
System.out.println(list1);
List list2 = new ArrayList(); list2.add("java"); list2.add("c++");
list1.addAll(1, list2); System.out.println(list1);
// 删除 list1.remove(0); System.out.println(list1);
// 修改 list1.set(0, "javax"); System.out.println(list1);
// 查 System.out.println(list1.get(0)); list1.add("apple"); list1.add("apple"); System.out.println(list1); System.out.println(list1.indexOf("apple")); System.out.println(list1.lastIndexOf("apple")); } |
ListIterator 继承于Iterator,在Iterator的基础上提供了以正向遍历集合,也能够以逆序遍历集合。
hasNext/next 以正向遍历
hasPrevious/previous 以逆序遍历
public static void main(String[] args) {
List list1 = new ArrayList(); list1.add("apple"); list1.add("banana"); list1.add("coco");
// 【1】快速遍历 System.out.println("--for each--"); for (Object item : list1) { System.out.println(item.toString()); }
// 【2】普通for System.out.println("--for--"); for(int i=0;i<list1.size();i++) { System.out.println(list1.get(i)); }
// 【3】集合迭代器 System.out.println("--iterator--"); Iterator it = list1.iterator(); while(it.hasNext()) { System.out.println(it.next()); }
System.out.println("--list iterator--"); // 正向遍历 ListIterator it2 = list1.listIterator(); while(it2.hasNext()) { System.out.println(it2.next()); }
// 逆序遍历 while(it2.hasPrevious()) { System.out.println(it2.previous()); }
System.out.println("--list iterator with index--"); ListIterator it3 = list1.listIterator(1); while(it3.hasNext()) { System.out.println(it3.next()); } } |
数据结构就是数据在内存中存储结构。根据存储的方式不一样,分为线性表、二叉树、图、栈、队列等
线性表数据按照必定的逻辑顺序存储在内存中。线性表是有序的。线性表根据内存的物理结构分为两种:数组和链表
数组是一种逻辑上有序的线性表,物理上也连续。
链表是一种逻辑上有序的线性表,但物理上不连续。
数组和链表的区别
相同点
不一样点
数组在查询时效率高,在添加、删除元素时效率低(涉及移动元素)
链表在查询时效率低(每次从头开始,不能跳跃访问),在添加、删除元素时效率高(不涉及移动元素)
特性:先进后出,后进先出
特性:先进先出
ArrayList 是List接口的实现类,底层数据结构是数组,实现大小可变的数组。
ArrayList 线程不安全,jdk1.2
ArrayList 底层数据结构是数组,默认数组大小是10,若是添加的元素个数超过默认容量,ArrayList会自动拓容,拓容原则:newCapacity = oldCapacity + oldCapacity / 2;
若是将来肯定序列的元素不在增长,经过调用trimToSize()调制容量至合适的空间。
ArrayList做为List接口的实现类,经常使用方法和遍历方法参考List接口。
Vector 是List接口的实现类,底层数据结构也是数组,也是大小可变的数组。
Vector是线程安全的,jdk1.0
Vector底层数据结构是数组,默认数组大小是10,若是添加的元素个数超过默认容量,Vector会自动拓容,拓容原则:newCapacity = oldCapacity +capacityIncrement(增加因子);若是将来肯定序列的元素不在增长,经过调用trimToSize()调制容量至合适的空间。
注意:Vector 在实现List接口的同时,同添加了自身特有的方法xxxElement,将来使用时为了程序的可拓展性,必定要按照接口来操做Vector。
LinkedList是List接口的实现类,底层数据结构是链表。
LinekList经常使用方法和遍历方法参照List接口。
LinkedList 线程不安全。
除了实现List接口, 还实现栈接口
|
push入栈操做 / pop出栈操做
public class Test01 { public static void main(String[] args) { LinkedList list = new LinkedList(); list.push("apple"); list.push("banana"); list.push("coco");
System.out.println(list.pop()); System.out.println(list.pop()); System.out.println(list.pop());
// java.util.NoSuchElementException System.out.println(list.pop()); } } |
队列(Queue)接口
add/remove/element() 可能会出现NoSuchElementException异常
public static void main(String[] args) {
LinkedList queue = new LinkedList(); // 入队 /** * 队列头 队列尾 *<----- <----- * [apple, banana, coco] */ queue.add("apple"); queue.add("banana"); queue.add("coco"); System.out.println(queue);
// 出队 System.out.println(queue.remove()); System.out.println(queue.remove()); System.out.println(queue.remove()); System.out.println(queue);
// java.util.NoSuchElementException System.out.println(queue.remove());
// 获取表头元素 System.out.println(queue.element()); } |
offer/poll/peek 可能会返回特殊值(null)
public static void main(String[] args) {
LinkedList queue = new LinkedList(); // 入队 /** * 队列头 队列尾 *<----- <----- * [apple, banana, coco] */ queue.offer("apple"); queue.offer("banana"); queue.offer("coco");
// 出队列 //System.out.println(queue.poll()); //System.out.println(queue.poll()); //System.out.println(queue.poll()); System.out.println(queue);
//System.out.println(queue.poll());
// 获取表头元素 System.out.println(queue.peek());
} |
双向队列(Deque)接口
/** * 以双向队列形式操做LinkedList */ public class Test04 { public static void main(String[] args) {
LinkedList queue = new LinkedList(); // 入队 /** *<----- <----- * [apple, banana, coco] * ----> -----> */
queue.addFirst("apple"); queue.addFirst("banana"); queue.addFirst("coco"); System.out.println(queue);
System.out.println(queue.removeLast()); System.out.println(queue.removeFirst()); System.out.println(queue.removeFirst()); System.out.println(queue);
// 获取头元素 System.out.println(queue.getFirst());
} } |
Iterator在迭代过程当中不容许向集合中添加元素
public static void main(String[] args) { ArrayList list = new ArrayList(); list.add("apple"); list.add("banana"); list.add("coco");
Iterator it = list.iterator(); while(it.hasNext()) { String item = (String) it.next(); if(item.equals("banana")) { list.add("test"); } }
System.out.println(list); } |
当经过Iterator集合迭代器遍历集合过程当中,不能再向集合汇总添加元素,不然出现ConcurrentModificationException 并发修改异常。
ListIterator容许程序员按任一方向遍历列表、迭代期间修改列表,并得到迭代器在列表中的当前位置
public class Test01 { public static void main(String[] args) { ArrayList list = new ArrayList(); list.add("apple"); list.add("banana"); list.add("coco");
ListIterator it = list.listIterator(); while(it.hasNext()) { String item = (String) it.next(); if(item.equals("banana")) { it.add("test"); } }
System.out.println(list); } } |
熟悉Iterator实现类的源码hasNext/next
泛型容许开发者在强类型程序设计语言(java)编写代码时定义一些可变部分,这些部分在使用前必须做出指明。
泛型就是将类型参数化
ArrayList<E> list表示声明了一个列表list,列表的元素是E类型 |
ArrayList<String> list = new ArrayList<String>(); 声明了一个列表list,列表的元素只能是String类型。 |
泛型在编译器起做用,运行时jvm察觉不到泛型的存在。
泛型在运行时已经被擦除了。
public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); list.add("apple"); System.out.println(list instanceof ArrayList); System.out.println(list instanceof ArrayList<String>); } |
Cannot perform instanceof check against parameterized type ArrayList<String>. Use the form ArrayList<?> instead since further generic type information will be erased at runtime |
当一个类中属性的数据类型不肯定时,具体是什么类型由使用者来肯定时,使用泛型。泛型类的形式
public class 类名<T> {
} |
定义一个泛型类
public class FanClass<T> { private T t;
public T getT() { return t; }
public void setT(T t) { this.t = t; }
public FanClass(T t) { super(); this.t = t; }
public FanClass() { super(); } }
|
public class Test01 { public static void main(String[] args) { FanClass<String> fan = new FanClass<String>(); fan.setT("apple");
FanClass<Integer> fan2 = new FanClass<Integer>(); fan2.setT(1); } }
|
当一个方法的参数类型不肯定时,具体是什么类型由使用者来肯定,能够考虑使用泛型方法。形式:
public <T> void xxx(T a) { System.out.println(a); } |
public class Student {
/*public void showInfo(int a) { System.out.println(a); }
public void showInfo(float a) { System.out.println(a); }
public void showInfo(String a) { System.out.println(a); }*/
public <T> void showInfo(T a) { System.out.println(a); } }
|
public static void main(String[] args) {
Student stu = new Student(); stu.showInfo(1); stu.showInfo("apple"); stu.showInfo(1.0f); } |
泛型方法在调用时肯定(指明)类型。
泛型方法在必定程度上优化了方法重载。
泛型方法能够定义多个泛型类型
// 能够定义多个泛型的类型 public <A,B> void showInfo(A a,B b) { System.out.println(a); System.out.println(b); } |
多个泛型类型进一步优化了方法重载。
多个同类型的泛型
// 多个同类型的泛型 /*public <A> void print(A a) { System.out.println(a); } public <A> void print(A a,A b) { System.out.println(a); System.out.println(b); }*/
public <A> void print(A...a) { System.out.println(a); } |
A… a 表示方法能够接受多个参数。当调用方法传递多个参数时,多个参数被放到a数组中,a是什么类型的数组由开发者调用处传参决定。
stu.print(1); stu.print(1,2);
stu.print("apple"); stu.print("apple","banana"); |
print(A...a) 方法称为可变参数的泛型形式。
若是接口中的方法的参数(形参、返回值)不肯定时,能够考虑使用泛型接口。形式
public interface FanInterface<T> { public void showInfo(T t); } |
[1]实现类能肯定泛型接口的类型
public class ImplClass implements FanInterface<String>{
public void showInfo(String t) { // TODO Auto-generated method stub
} } |
[2]实现类不能肯定泛型接口的类型->继续泛。
public class ImplClass2<T> implements FanInterface<T>{
public void showInfo(T t) {
} } |
public static void print(ArrayList<? extends Pet> list) { for (Pet pet : list) { pet.showInfo(); } } |
泛型的上限ArrayList(? extends Pet) list 声明了一个容器,容器中的元素类型必定要继承于Pet,咱们称这种形式叫作泛型的上限。
泛型的下限ArrayList(? super Pet) list 声明了一个容器,容器中的元素类型必定要是Pet的父类,咱们称这个形式为泛型的下限。
Set接口表示一个惟1、无序的容器(和添加顺序无关)
public static void main(String[] args) { /** * 增:add/addAll * 删:clear/remove/removeAll/retainAll * 改: * 查:contains/containsAll * 遍历:iterator * 其余:size/isEmpty */
Set<Integer> set = new HashSet<Integer>(); // [1]添加 // 无序 set.add(10); set.add(3); set.add(20); set.add(0); // 不能添加剧复元素 boolean r = set.add(1); System.out.println(set);
// 【2】删除 // set.remove(1); // set.clear(); // System.out.println(set);
// 【3】查看是否包含 System.out.println(set.contains(1));
// 【4】其余 System.out.println(set.size()); System.out.println(set.isEmpty()); } |
public static void main(String[] args) {
Set<String> set = new HashSet<String>(); set.add("banana"); set.add("apple"); set.add("coco");
// 快速遍历 for (String item : set) { System.out.println(item); }
// 迭代器 Iterator<String> it = set.iterator(); while(it.hasNext()) { String item = it.next(); System.out.println(item); } } |
Set接口的实现类常见的有HashSet、LinkedHashSet、TreeSet
HashSet是Set接口的实现类,底层数据结构是哈希表。
HashSet是线程不安全的(不保证同步)
根据哈希表的工做原理,请存储一个自定义对象到HashSet中。
package cn.sxt03.hashset;
public class Student { private String id; private String name; private int age;
// …
public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; }
public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Student other = (Student) obj; if (age != other.age) return false; if (id == null) { if (other.id != null) return false; } else if (!id.equals(other.id)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; }
public String toString() { return "Student [id=" + id + ", name=" + name + ", age=" + age + "]"; }
}
|
总结
[1]若是向HashSet中存储元素时,元素必定要实现hashCode方法和equals方法。
[2] 优势:添加、删除、查询效率高;缺点:无序
LinkedHashSet是Set接口的实现类,底层数据结构哈希表+链表
哈希表用于散列元素;链表用于维持添加顺序。
若是要添加自定义对象元素,也须要重写hashCode和equals方法。
TreeSet 是Set接口的实现类,底层数据结构是二叉树。
TreeSet 存储的数据按照必定的规则存储。存储规则让数据表现出天然顺序。
添加一个新元素t的存储的步骤
[1] 若是集合无元素,t直接加入;若是集合有元素,t和根节点比较;
[2] 若是t小于根节点;把t放到根节点的左子树上;重复1-3步骤
[3] t大于根节点;把t放到根节点的右子树上;重复1-3步骤
输出时按照必定的规则:左子树->根节点->右子树
根据TreeSet的工做原理,向TreeSet添加自定义元素?
向TreeSet中添加元素时,必定要提供比较策略,不然会出现ClassCastException。
比较策略分两种:内部比较器和外部比较器
当一个自定义对象实现Comparable并实现compareTo方法时,经过指定具体的比较策略,此时称为内部比较器。
package cn.sxt05.treeset;
public class Student implements Comparable<Student>{ private String id; private String name; private int age;
// 。。。
@Override public String toString() { return "Student [id=" + id + ", name=" + name + ", age=" + age + "]"; }
@Override public int compareTo(Student o) { if(this.getAge()<o.getAge()) { return -1; }else if(this.getAge() == o.getAge()) { return 0; }else { return 1; } }
}
|
比较策略的几种状况
[1]比较策略通常当前对象写在前面,待比较对象也在后面,比较结果默认升序
return this.getAge() - o.getAge() ; |
若是想要降序,改变两个比较对象的位置便可。
[2] 多种比较因素
@Override public int compareTo(Student o) { /*if(this.getAge()<o.getAge()) { return -1; }else if(this.getAge() == o.getAge()) { return 0; }else { return 1; }*/
// return this.getAge() - o.getAge() ;
if(this.getAge()<o.getAge()) { return -1; }else if(this.getAge() == o.getAge()) { return this.getName().compareTo(o.getName()); }else { return 1; } } |
当实际开发过程当中不知道添加元素的源代码、无权修改别人的代码,此时能够使用外部比较器。
Comparator 位于java.util包中,定义了compare(o1,o2) 用于提供外部比较策略。
TreeSet接受一个指定比较策略的构造方法,这些比较策略的实现类必须实现Comparator
接口。
需求:按照字符串的长度比较
public class Test01 { public static void main(String[] args) {
LenComparator lenComparator = new LenComparator(); TreeSet<String> set2 = new TreeSet<String>(lenComparator);
set2.add("banana"); set2.add("coco"); set2.add("apple");
set2.add("apple"); System.out.println(set2);
} }
class LenComparator implements Comparator<String>{
@Override public int compare(String o1, String o2) { return o1.length() - o2.length(); } } |
使用匿名内部类优化
public class Test02 { public static void main(String[] args) {
TreeSet<String> set2 = new TreeSet<String>(new Comparator<String>() {
@Override public int compare(String o1, String o2) { return o1.length() - o2.length(); }
});
set2.add("banana"); set2.add("coco"); set2.add("apple");
set2.add("apple"); System.out.println(set2);
} } |
Map接口称为键值对集合或者映射集合,其中的元素(entry)是以键值对(key-value)的形式存在。
Map 容器接口中提供了增、删、改、查的方式对集合进行操做。
Map接口中都是经过key来操做键值对,通常key是已知。经过key获取value。
public static void main(String[] args) {
/** * 增:put/putAll * 删:clear/remove * 改:put * 查:get/containsKey/containsValue * 其余:isEmpty/size */
Map<String, String> map = new HashMap<String,String>();
// 【1】put map.put("A", "apple"); map.put("B", "banana"); map.put("C", "coco");
// 【2】删除 // map.clear(); // smap.remove("A");
// 【3】修改 //map.put("A", "apple x");
// 【4】查看 String val = map.get("A"); System.out.println(map.containsKey("D"));
System.out.println(map); } |
经过keySet() 返回map中键的set集合。
public static void main(String[] args) {
Map<String, String> map = new HashMap<String,String>();
map.put("B", "banana"); map.put("A", "apple"); map.put("C", "coco"); // map无序 // 能够根据key的天然顺序 让map有序 => 通常用string做为key System.out.println(map);
// 遍历 Set<String> keys = map.keySet(); for (String key : keys) { System.out.println(key+"=>"+map.get(key)); }
Iterator<String> it = keys.iterator(); while(it.hasNext()) { String key = it.next(); System.out.println(key+"=>"+map.get(key)); } } |
map中以键值对做为元素,键值对在map中称为entry,entrySet返回键值对的set集合。
public static void main(String[] args) {
Map<String, String> map = new HashMap<String,String>();
map.put("B", "banana"); map.put("A", "apple"); map.put("C", "coco"); // map无序 // 能够根据key的天然顺序 让map有序 => 通常用string做为key System.out.println(map);
// entrySet Set<Entry<String, String>> entrySet = map.entrySet(); for (Entry<String, String> entry : entrySet) { System.out.println(entry.getKey()+"=>"+entry.getValue()); }
Iterator<Entry<String, String>> it2 = entrySet.iterator(); while(it2.hasNext()) { Entry<String, String> entry = it2.next(); System.out.println(entry.getKey()+"=>"+entry.getValue()); } } |
Map接口的实现类HashMap、LinkedHashMap、TreeMap
HashMap 是Map的实现类,key以HashSet存储。
public static void main(String[] args) {
/* HashMap<String, Object> map = new HashMap<String,Object>();
ArrayList<String> list1 = new ArrayList<String>(); list1.add("alex"); list1.add("alice"); list1.add("allen"); map.put("A", list1);
ArrayList<String> list2 = new ArrayList<String>(); list2.add("ben"); list2.add("bill"); map.put("B", list2);
System.out.println(map); */
HashMap<Student, Object> map = new HashMap<Student,Object>();
ArrayList<String> list1 = new ArrayList<String>(); list1.add("alex"); list1.add("alice"); list1.add("allen"); Student s1 = new Student("001", "大狗", 20); map.put(s1, list1);
ArrayList<String> list2 = new ArrayList<String>(); list2.add("ben"); list2.add("bill"); Student s2 = new Student("001", "大狗", 20); // 修改 map.put(s2, list2); System.out.println(map);
} |
总结:
[1] 向HashMap中存储元素时,key必定要实现hashCode和equals
[2] 通常建议使用String做为Map接口的key
LinkedHashMap是Map接口的实现类,key以LinkedHashSet存储。
哈希表散列key,链表维持key的添加顺序。
public static void main(String[] args) {
/*LinkedHashMap<String, Object> map = new LinkedHashMap<String,Object>();
ArrayList<String> list2 = new ArrayList<String>(); list2.add("ben"); list2.add("bill"); map.put("B", list2);
ArrayList<String> list1 = new ArrayList<String>(); list1.add("alex"); list1.add("alice"); list1.add("allen"); map.put("A", list1);
System.out.println(map);*/
HashMap<Student, Object> map = new HashMap<Student,Object>();
ArrayList<String> list1 = new ArrayList<String>(); list1.add("alex"); list1.add("alice"); list1.add("allen"); Student s1 = new Student("001", "大狗", 20); map.put(s1, list1);
ArrayList<String> list2 = new ArrayList<String>(); list2.add("ben"); list2.add("bill"); Student s2 = new Student("001", "大狗", 20); // 修改 map.put(s2, list2); System.out.println(map);
} |
TreeMap是Map的实现类,key以TreeSet存储。
public static void main(String[] args) {
/*TreeMap<String, Object> map = new TreeMap<String,Object>(new Comparator<String>() {
@Override public int compare(String o1, String o2) { return o1.length() - o2.length(); } });
ArrayList<String> list2 = new ArrayList<String>(); list2.add("ben"); list2.add("bill"); map.put("Aa", list2);
ArrayList<String> list1 = new ArrayList<String>(); list1.add("alex"); list1.add("alice"); list1.add("allen"); map.put("B", list1);
System.out.println(map);*/
TreeMap<Student, Object> map = new TreeMap<Student,Object>(new Comparator<Student>() {
@Override public int compare(Student o1, Student o2) { return o1.getAge() - o2.getAge(); } });
ArrayList<String> list1 = new ArrayList<String>(); list1.add("alex"); list1.add("alice"); list1.add("allen"); Student s1 = new Student("001", "大狗", 20); map.put(s1, list1);
ArrayList<String> list2 = new ArrayList<String>(); list2.add("ben"); list2.add("bill"); Student s2 = new Student("001", "2狗", 20); // 修改 map.put(s2, list2); System.out.println(map);
} |
总结