1.容器:數據封裝(數組,StringBuilder,對象,集合)
數值很多,用數組存。
數組很多,用二維數組存。
數據很多,用對象存。
對象有很多,有集合存。
數組是固定長度,集合是可變長度。
2.集合:
1.基本內容
1.集合類的由來:
對象用於封裝特有數據,對象多了需要存儲,如果對象的個數不確定。
就使用集合容器進行存儲。
2.集合特點:
1.用於存儲對象的容器。
2.集合的長度是可變的。
3.集合中不可以存儲基本數據類型值。
3.集合容器因爲內部的數據結果不同,有多種具體容器。
不斷向上抽取,就形成了集合框架。
4.集合框架的頂層是Collection根接口,包含共性內容:
集合在工具包java.util裏
1.添加
boolean add(Object obj);
boolean addAll(Collection coll);
2.刪除
boolean remove(Object obj);
boolean removeAll(Collection coll); //將兩個集合中的相同元素從調用removeAll的集合中刪除
void clear(); 清空
3.判斷
boolean contains(Object obj); //集合是否包含對象
boolean containsAll(Collection coll); //集合是否包含集合
boolean isEmpty(); 判斷集合中是否有元素
4.獲取
int size();
Iterator iterator(); 取出元素的方式:迭代器。
該對象必須依賴於具體容器,因爲每一個容器的數據結構不同。
所以該迭代器對象是在容器中進行內部實現的。
對於容器使用者而言,具體的實現不重要,只要通過容器獲取到該實現的迭代器的對象即可。
也就是iterator()方法。
Iterator接口就是對所有的Collection容器進行元素取出的公共接口。
5.其他:
boolean retainAll(Collection coll); //取兩個集合的交集,和removeAll功能相反
Object[] toArray(); 將集合轉成數組
2.集合框架的構成圖
3.List集合接口:有序
特點:
1.存入和取出的順序一致,元素都有索引
2.元素可重複
特有的常見方法:
1.添加:
void add(index,element);
void addAll(index, collection);
2.刪除:
Object remove(index);
3.修改:
Object set(intdex,element);
4.獲取:
Object get(index); //list特有的取出方式之一
int indexOf(Object);
int lastIndexOf(Object);
List subList(from,to);
ListIterator接口:Iterator子接口,存在增刪改查方法
解決在使用迭代器操作集合的同時,又在使用集合本身操作集合,而出現的異常。
List list = new ArrayList();
list.add("abc1");
list.add("abc2");
/*
Iterator it = list.lterator();
while(it.hasNext()){
Object obj = it.next();
if(obj.equals("abc2")){
list.add("abc3"); //在迭代器過程中,不用使用集合操作元素,容易出現異常。
}
}*/
ListIterator it = list.listIterator();
//它可以實現在迭代過程中完成對元素的增刪改查。
//注意只有list集合具備該迭代功能
while(it.hasNext()){
Object obj = it.next();
if(obj.equals("abc2")){
it.add("abc3"); //正確處理
}
}
/*減少內存寫法:
for(Iterator it = coll.iterator();it.hasNext();){
System.out.println(it.next);
}
*/
List接口常用的實現類:Vector, ArrayList, LinkedList
1.Vector: 內部是數組數據結構,是同步的。(數組100%延長,基本不用了)增刪,查詢都很慢。
2.ArrayList:內部是數組數據結構,是不同步的。替代了Vector。數組50%延長。查詢的速度快(空間是連續的)。增刪慢(一動而動全身)
3.LinkedList:內部是鏈表數據結構,是不同步的。增刪元素的速度很快,查詢慢(空間不是連續的)。元素也是有編號的的,只是不連續。
LinkedList:
addFirst();
addLast();
jdk1.6
offerFirst();
offerLast();
getFirst(); //獲取但不移除,如果鏈表爲空,拋出NoSuchElementException
getLast();
jdk1.6
peekFirst(); //獲取但不移除,如果鏈表爲空,返回Null,可以做判斷
peekLast();
removeFirst();//獲取並移除,如果鏈表爲空,拋出NoSuchElementException
removeLast();
jdk1.6
pollFirst(); //獲取並移除,如果鏈表爲空,返回Null,可以做判斷
pollLast();
Vector接口中獲取元素的方法 :
Enumeration elements();返回一個枚舉類型對象。
API:
接口 Enumeration的功能與 Iterator 接口的功能是重複的。此外,Iterator 接口添加了一個可選的移除操作,並使用較短的方法名。新的實現應該優先考慮使用 Iterator 接口而不是 Enumeration 接口。
Vector v = new Vector();
v.addElement("abc1");
v.addElement("abc1");
/*
Enumeration en = v.elements();
while(en.hasMoreElement()){
System.out.println(en.nextElement());
}*/
Iterator it = it.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
LinkedList :
LinkeList link = new LinkeList();
link.addFirst("abc1");
link.addFirst("abc2");
link.addFirst("abc3");
System.out.println(link.getFirst()); //獲取第一個但不刪除
System.out.println(link.removeFirst()); //獲取第一個並刪除
while(!link.isEmpty()){
System.out.println(link.removeFirst()); //不用迭代器輸出對象,但是會刪除集合對象。
}
Iterator it = link.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
//LinkedList集合練習題:
/*使用LinkedList模擬一個堆棧或者隊列數據結構。
堆棧:先進後出 First In Last Out FIFO
隊列:先進先出 First In First Out FIFO
應該描述這樣一個容器,給使用者提供一個容器對象完成這兩種結構中的一種。
*/
//工具類
class DuiLie{
private LinkedList link;
public DuiLie(){
link = new LinkedList();
}
public void myAdd(Object obj){
link.addLast(obj);
}
public Object myGet(){
return link.removeFirst();
}
pubilc boolean isNull(){
return link.isEmpty();
}
}
ArrayList集合存儲自定義對象:
public class ArrayListTest {
public static void main(String[] args) {
ArrayList al = new ArrayList();
al.add(new Person("list1",21));//public boolean add(Object obj),所以後面需要強轉
al.add(new Person("list2",22));
al.add(new Person("list3",23));
al.add(new Person("list4",24));
Iterator it = al.iterator();
while(it.hasNext()){
// System.out.println(((Person)it.next()).getName()+ " : "+ ((Person) it.next()).getAge());
Person p = (Person) it.next(); //強轉
System.out.println(p.getName() + " : " + p.getAge());
}
//al.add(5); //自動裝箱
}
}
//ArrayList集合練習題:
ArrayList集合在判斷元素是否相同,使用的是元素的equals()方法,只需覆蓋equals()方法即可。
而hashSet則需要覆蓋hashCode()方法和equals()方法。
數據結構不一樣,對元素的判斷依據也不一樣。
//定義功能去除ArrayList中的重複元素。
//存儲字符串的情況:
public static ArrayList getSingleElement(ArrayList al){
//1.定義一個臨時容器
ArrayList temp = new ArrayList();
//2.迭代al集合
Iterator it = al.iterator();
while(it.hasNext()){
Object obj = (Object) it.next();
//3.判斷被迭代的元素是否在臨時容器中存在
if(!temp.contains(obj))
temp.add(obj);
}
return temp;
}
//存儲自定義對象的情況:在自定義類中要重寫equals()方法
public static ArrayList getSingleElement(ArrayList al){
//1.定義一個臨時容器
ArrayList temp = new ArrayList();
//2.迭代al集合
Iterator it = al.iterator();
while(it.hasNext()){
Person p = (Person) it.next();
//3.判斷被迭代的元素是否在臨時容器中存在
if(!temp.contains(p)) //比較的原理是調用了object的equals方法。
temp.add(p);
}
return temp;
}
public boolean equals(Object obj) {
Person p = (Person) obj;
return this.name.equals(p.name) && this.age == p.age;
}
4.Set集合接口:不重複
特點:
1.元素不能重複
2.無序(有可能“有序”)
set接口中的方法和Collection一致。
|--HashSet:內部數據結構是哈希表(實際上是一個 HashMap 實例) ,是不同步的。
是通過對象的hashcode和equals方法來判斷對象唯一性。
如果對象的hashcode值不同,那麼不用判斷equals方法,直接存儲到哈希表中。
如果對象的hashcode值相同,那麼要再次判斷對象的equals方法是否爲true。
如果爲true,視爲相同元素,不存。如果爲false,視爲不同元素,就進行存儲。
記住,如果元素要存儲到HashSet集合中,必須覆蓋hashCode方法和equals方法。
一般情況下,如果定義的類會產生很多對象,比如人,學生,書,通常都需要覆蓋equals方法,hashcode方法,建立對象判斷是否相同的依據。
|--TreeSet:可以對set集合中的元素進行排序。是不同步的。
判斷元素唯一性的方式:就是根據比較方法的返回結果是否是0,是0,就是相同元素,不存。
①TreeSet對元素進行排序的方式一:
讓元素自身具備比較功能,元素需要實現comparable接口。覆蓋compareTo方法。
如果不要按照對象中具備的自然順序進行排序。如果對象中不具備自然順序。
②可以使用TreeSet集合的第二種排序方式二:
讓集合自身具備比較功能。在集合創建對象時完成。定義一個類實現Comparator接口,覆蓋compare方法。將該類的對象作爲參數傳遞給TreeSet集合的構造函數。
TreeSet集合內部數據結構:二叉樹(紅黑樹)(每次插入元素前都會默認記錄折中元素的位置,採用二分查找提高效率)
HashSet集合存儲自定義對象:
public class HashSetTest {
public static void main(String[] args) {
HashSet hs = new HashSet();
hs.add(new Person("list1",24));
hs.add(new Person("list2",25));
hs.add(new Person("list3",26));
hs.add(new Person("list2",25));
Iterator it = hs.iterator();
while(it.hasNext()){
Person p = (Person) it.next();
System.out.println(p.getName()+" : "+ p.getAge());
}
}
}
public class Person {
private String name;
private int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
// System.out.println(this+ ".......hashcode");
return name.hashCode() + age*38; //減少哈希值相同概率
}
@Override
public boolean equals(Object obj) {
// System.out.println(this+ "....equals..." + obj);
if(this == obj)
return ture;
if(obj instanceof Person)
throw new ClassCastException("類型錯誤");
Person p = (Person) obj;
return this.name.equals(p.name) && this.age == p.age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String toString()
{
return name +": "+age;
}
}
LinkedHashSet:由哈希表和鏈表實現。有序,怎麼存怎麼取。(注意和排序區別)。
所以判斷用什麼集合,無關有無順序。需要唯一性用set集合,不需要就用list集合。
HashSet hs = new LinkedHashSet();
hs.add("abc1");
hs.add("abc2");
hs.add("abc3");
Iterator it = hs.iterator();
while(it.hasNext()){
System.out.println(it.next()); //順序輸出
}
TreeSet集合對元素進行排序的方式一:存儲自定義對象時需要實現 Comparable接口,複寫compareTo()方法。
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet<Person> ts =new TreeSet<Person>();
ts.add(new Person("zhangsan",28));
ts.add(new Person("wangwu",21));
ts.add(new Person("lisi",22));
ts.add(new Person("zhaoliu",21));
Iterator<Person> it = ts.iterator();
while(it.hasNext()){
Person p =it.next();
System.out.println(p.getName()+":"+ p.getAge());
}
// demo1(ts);
}
public static void demo1(TreeSet ts) {
ts.add("abc"); //字符串本身就重寫了compareTo方法。
ts.add("zaa");
ts.add("aa");
ts.add("nba");
ts.add("dwe");
Iterator it = ts.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
public class Person implements Comparable<Person>{ //實現Comparable接口
private String name;
private int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public int compareTo(Person p) { //重寫compareTo方法。按年齡排序
// Person p = (Person)o; //元素比較傳入一個參數
int temp = this.age - p.age;
return temp==0?this.name.compareTo(p.name):temp;
}
}
比較器Comparator:更常用
(如果不要按照對象中具備的自然順序進行排序。如果對象中不具備自然順序。)
TreeSet集合對元素進行排序的方式二:讓集合自身具備比較功能。在集合創建對象時完成。
定義一個類實現Comparator接口,覆蓋compare方法。將該類的對象作爲參數傳遞給TreeSet集合的構造函數。
TreeSet有序化:如果compare返回值固定是正數,則會按存入順序輸出,如果是負數,則逆序輸出。
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet ts =new TreeSet(new ComparatorByName()); //使用比較器自定義排序功能
ts.add(new Person("zhangsan",28));
ts.add(new Person("wangwu",21));
ts.add(new Person("lisi",22));
ts.add(new Person("zhaoliu",21));
Iterator it = ts.iterator();
while(it.hasNext()){
Person p =(Person) it.next();
System.out.println(p.getName()+":"+ p.getAge());
}
}
public class ComparatorByName implements Comparator {
@Override
public int compare(Object o1, Object o2) { //集合自身比較需要傳2個參數
Person p1 = (Person)o1;
Person p2 = (Person)o2;
int temp = p1.getName().compareTo(p2.getName());
return temp ==0?p1.getAge() - p2.getAge():temp;
}
}
3.Map集合:和Collection一樣的頂層接口。
Map:一次添加一對元素。Collection一次添加一個元素。
Map也稱爲雙列集合,Collection稱爲單列集合。
其實Map集合中存儲的就是鍵值對。
Map集合中必須保證 鍵 的唯一性。
常用方法:
1.添加
value put(key, value); 返回前一個和key關聯的值,如果沒有返回null
2.刪除
void clear(); 清空map集合
value remove(key); 根據指定的鍵刪除這個鍵值對
3.判斷
boolean containKey(key);
boolean containValue(value);
boolean isEmpty();
4.獲取
value get(key);通過鍵獲取值。如果沒有該鍵返回null。
當然可以通過返回null,來判斷是否包含指定鍵。
int size(); 獲取鍵值對的個數。
取出map中的所有元素:
①keySet()方法:返回所包含的鍵的set視圖。返回set集合。
public class MapDemo {
public static void main(String[] args) {
Map<Integer,String> map = new HashMap<Integer,String>();
method(map);
}
public static void method(Map<Integer,String> map){
map.put(8, "wangwu");
map.put(3, "lisan");
map.put(2, "sunhao");
map.put(6, "tiantian");
//取出map中的所有元素
//原理:通過keyset方法獲取map中所有的鍵所在的set集合,再通過set的迭代器獲取到每一個鍵,
//再對每一個鍵通過map集合的get方法獲取其對應的值即可
Set<Integer> keySet = map.keySet();
Iterator<Integer> it = keySet.iterator();
while(it.hasNext()){
Integer key = it.next();
String value = map.get(key);
System.out.println(key+":"+value);
}
}
}
②entrySet()方法:返回所包含的映射關係的set視圖。返回set集合。
/*
通過Map轉換成set就可以迭代。
通過entrySet方法 將鍵和值的映射關係作爲對象存儲到了Set集合中,而這個映射關係的類型就是Map.Entry類型。
*/
Set<Map.Entry<Integer, String>> entrySet = map.entrySet();
Iterator<Map.Entry<Integer, String>> it = entrySet.iterator();
while(it.hasNext()){
Map.Entry<Integer, String> me = it.next();
System.out.println(me.getKey()+":"+me.getValue());
}
③values():返回此映射中包含的值的 Collection 視圖。
Collection<String> values = map.values();
Iterator<String> it = values.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
Map集合的常見子類對象:1.0時單列集合就是Vector,雙列集合就是Hashtable。
1.Hashtable:內部機構是哈希表,是同步的。不允許null作爲鍵和值。
類Properties:用來存儲鍵值對型的配置文件的信息。可以和IO技術相結合。
2.HashMap:內部結構是哈希表,是不同步的。允許null作爲鍵和值。
3.TreeMap:內部結構是二叉樹,是不同步的。可以對Map集合中的鍵進行排序。
LinkedHashMap:由哈希表和鏈表實現。有序,怎麼存怎麼取。(注意和排序區別)。
Map集合練習:
/*
* 練習:
* "fdgavcbsacdfs"獲取該字符串中嗎,每一個字母出現的次數。
* 要求打印的結果是:a(2)b(1)...
*
* 對於結果的分析發現,字母和次數之間存在映射關係,而且這種關係很多。
* 很多就需要存儲,能存儲映射關係的容器有數組和Map集合。
*
* 關係中沒有一方是有序編號,所以用Map集合。
* 因爲保證唯一性的一方具有順序a,b...,所以使用TreeMap集合。
*
*這個集合最終存儲的是字母和次數的對應關係。
*
*1.因爲操作的是字符串中的字母,所以先將字符串變成字符數組。
*2.遍歷字符數組,用每一個字母作爲鍵去查Map集合這個表。
*如果該字母鍵不存在,就將該字母作爲鍵,1作爲值存儲到map集合中。
*如果該字母鍵存在,就將該字母鍵的值取出並+1,再將該字母和+1後的值存儲到map集合中。鍵相同值會覆蓋。
*3.遍歷結束,map集合就記錄了所有字母的次數。
*/
public class Test {
public static void main(String[] args) {
String str = "f --2dgavcbsacdfs";
String s = getCharCount(str);
System.out.println(s);
}
public static String getCharCount(String str){
//將字符串變成字符數組
char[] chs = str.toCharArray();
//定義map集合表
Map<Character,Integer> map = new TreeMap<Character, Integer>();
for (int i = 0; i < chs.length; i++) {
if(!(chs[i]>='a' && chs[i] <='z' || chs[i]>='A' && chs[i] <='Z'))
continue;
//將數組中的字符作爲鍵去查map表。
Integer value = map.get(chs[i]);
int count = 1; //簡化代碼
//判斷值是否爲null
if(value!=null)
{
count = value + 1;
}
map.put(chs[i],count);
/*
if(value ==null){
map.put(chs[i],1);
}
else{
map.put(chs[i], value+1);
}*/
}
//return map.toString();
return mapToString(map);
}
private static String mapToString(Map<Character, Integer> map) {
StringBuilder sb = new StringBuilder();
Iterator<Character> it = map.keySet().iterator();
while(it.hasNext()){
Character key = it.next();
Integer value = map.get(key);
sb.append(key +"("+ value +")");
}
return sb.toString();
}
}
Map在有映射關係時,可以優先考慮。
在查表法中的應用較爲多見。
4.泛型:jdk1.5出現的安全機制。編譯時期的安全技術。
好處:
1.將運行時期的問題ClassCastException轉到了編譯時期。
2.避免了強制轉換的麻煩。
<>:
什麼時候用:當操作的引用數據類型不確定的時候,就是用<>。將操作的引用數據類型傳入即可。
其實<>就是一個用於接收具體引用數據類型的參數範圍。
在程序中,只要用到了帶有<>的類或接口,就要明確傳入的具體引用數據類型。
泛型的 擦除和補償:
泛型技術是給編譯器使用的技術,用於編譯時期,確保了類型的安全。
擦除:運行時,會將泛型去掉,生成的class文件中是不帶泛型的,這個稱爲泛型的擦除。
爲什麼擦除:爲了兼容運行的類加載器。
補償:在運行時,通過獲取元素的類型進行轉換動作。不用使用者再強制轉換。
泛型在集合中的應用:
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet<Person> ts =new TreeSet<Person>();
ts.add(new Person("zhangsan",28));
ts.add(new Person("wangwu",21));
ts.add(new Person("lisi",22));
ts.add(new Person("zhaoliu",21));
Iterator<Person> it = ts.iterator();
while(it.hasNext()){
Person p =it.next();
System.out.println(p.getName()+":"+ p.getAge());
}
}
public class Person implements Comparable<Person>{ //實現Comparable接口
private String name;
private int age;
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int compareTo(Person p) { //重寫compareTo方法。按年齡排序
// Person p = (Person)o; //元素比較傳入一個參數
int temp = this.age - p.age;
return temp==0?this.name.compareTo(p.name):temp;
}
}
泛型類:
/*
*在jdk1.5後,使用泛型來接收類中要操作的引用數據類型。
*泛型類,什麼時候用:當類中的操作的引用數據類型不確定的時候,就使用泛型類表示。
*
*/
public class Tool<Q>{
private Q q;
public Q getObject() {
return q;
}
public void setObject(Q object) {
this.q = object;
}
}
/*public class Tool {
private Object object;
public Object getObject() {
return object;
}
public void setObject(Object object) {
this.object = object;
}
}*/
public class GenericDefineDemo3 {
public static void main(String[] args) {
Tool<Student> tool = new Tool<Student>(); //使用泛型類
tool.setObject(new Student()); //如果傳入的對象不對,編譯報錯
Student stu = tool.getObject();
/* Tool tool = new Tool();
tool.setObject(new Worker());
Student stu = (Student)tool.getObject();*/
}
}
泛型方法:<>放在修飾符的後面,返回值的前面。
一旦使用了泛型,只能使用Object的方法。
//將泛型定義在方法上
public <W> void show(W str){
System.out.println("show:" + str);
}
public <Q>void print(Q str){
System.out.println("print:"+ str);
}
/*
//當方法靜態時,不能訪問 類 上定義的泛型,
//如果靜態方法使用泛型,只能將泛型定義在方法上。
public static void method(Q str){
System.out.println("method:"+ str);
}
//正確的寫法:
public static<Y> void method(Y str){
System.out.println("method:"+ str);
}
*/
public static void main(String[] args) {
Tool<String> tool = new Tool<String>();
tool.show("abc"); //自動選擇參數類型
tool.print(new Integer(4)); //報錯,print方法是根據Tool類的泛型來確定
}
泛型接口:
interface Inter<T>{
public void show(T t);
}
class InterImpl implements Inter<String>{
public void show(String str){
System.out.println("show :" + str);
}
}
class InterImpl2 implements Inter<Q>{
public void show(Q q){
System.out.println("show :" + q);
}
}
public static void main(String[] args){
InterImpl in = new InterImpl();
in.show("haha");
InterImpl2<Integer> in2 = new InterImpl2<Integer>();
in2.shoe(5);
}
泛型的高級應用:泛型的限定
泛型的通配符:?未知類型
public class GenericDemo4 {
public static void main(String[] args) {
ArrayList<String> al = new ArrayList<String>();
al.add("abc");
al.add("hehe");
ArrayList<Integer> al2 = new ArrayList<Integer>();
al2.add(55);
al2.add(67);
printCollection(al);
printCollection(al2);
}
/**
* 迭代並打印集合中的元素
* @param al
*/
//泛型的通配符?
private static void printCollection(Collection<?> al) {
Iterator<?> it = al.iterator();
while(it.hasNext()){
// T t = it.next();
System.out.println(it.next().toString());
}
}
/*
private static <T> void printCollection(Collection<T> al) {
Iterator<T> it = al.iterator();
while(it.hasNext()){
T t = it.next();
System.out.println(t.toString());
}
}*/
}
泛型的限定:? ( 就是 ? extends Object)
/*
?extends E :接收E類型或者E的子類型對象。上限。
?super E: 接收E類型或者E類型的父類型。下限。
*/
private static void printCollection(Collection<? extends Person> al) {
Iterator<? extends Person> it = al.iterator();
while(it.hasNext()){
// T t = it.next();
Person p = it.next();
System.out.println(p.toString());
}
}
泛型限定在集合中的應用:
上限的體現:Collection中的addAll()方法。
boolean addAll(Collection<? extends E> c)
將指定 collection 中的所有元素都添加到此 collection 中(可選操作)。
原因:如果不限定,則所有的對象都可以添加,這時在取出的時候需要強制轉換,容易出現問題。
一般在存儲元素的時候,都使用上限。因爲這樣取出都是按照上限類型來運算的,不會出現類型安全隱患。
下限的體現:TreeSet中的構造方法:
TreeSet(Comparator<? super E> comparator)
構造一個新的空 TreeSet,它根據指定比較器進行排序。
通常對集合中的元素進行取出操作時,可以用下限。用得較少。
通配符?的體現:Collection中的containsAll()方法。
boolean containsAll(Collection<?> c)
如果此 collection 包含指定 collection 中的所有元素,則返回 true。
因爲equals方法比較兩邊可以是任意對象。
5.集合查閱的技巧:
需要唯一麼?
需要:Set
需要指定順序麼?
需要:TreeSet
不需要:HashSet
但是想要一個和存儲一致的順序:LinkedHashSet
不需要:List
需要頻繁增刪麼?
需要:LinkedList
不需要:ArrayList
如何記住每一個容器的結果和所屬體系?
後綴名就是該集合所屬的體系:
List:
ArrayList
LinkedList
Set:
HashSet
TreeSet
前綴名就是該集合的數據結構:
Array:就要想到數組,想到查詢快,有角標
Link:就要想到鏈表,想到增刪快,想到add/get/remove + first/last的方法
Hash:就要想到哈希表,想到唯一性,想到元素需要覆蓋hashcode方法和equals方法。
tree:就要想到二叉樹,想到排序,想到兩個接口Comparable,Comparator。
通常這些常用的集合都是不同步的。
6.集合框架工具類:
Collections: 操作集合的工具類
1.排序:
/*
static <T extends Comparable<? super T>> void sort(List<T> list)
根據元素的自然順序 對指定列表按升序進行排序。
static <T> void sort(List<T> list, Comparator<? super T> c)
根據指定比較器產生的順序對指定列表進行排序。
static void swap(List<?> list, int i, int j)
在指定列表的指定位置處交換元素。
*/
public class CollectionsDemo {
public static void main(String[] args) {
/*
* Collections:是集合框架的工具類。
* 裏面的方法都是靜態的。
*/
demo_1();
}
public static void demo_1(){
List<String> list = new ArrayList<String>();
list.add("abc");
list.add("cba");
list.add("aa");
list.add("zzz");
list.add("cba"); //有重複的,不能用TreeSet
list.add("nbaa");
System.out.println(list);
//對list集合進行指定順序的排序。
Collections.sort(list);
System.out.println(list);
Collections.sort(list,new ComparatorByLength());
System.out.println(list);
//源碼實現:
mySort(list);
System.out.println(list);
mySort1(list,new ComparatorByLength());
System.out.println(list);
}
public static <T> void mySort1(List<T> list, Comparator<? super T> comp){
for (int i = 0; i < list.size()-1; i++) {
for (int j = i+1; j < list.size(); j++) {
if(comp.compare(list.get(i), list.get(j))>0){
// T temp = list.get(i);
// list.set(i, list.get(j));
// list.set(j, temp);
Collections.swap(list, i, j);
}
}
}
}
public static<T extends Comparable<? super T>> void mySort(List<T > list){
for (int i = 0; i < list.size()-1; i++) {
for (int j = i+1; j < list.size(); j++) {
if(list.get(i).compareTo(list.get(j))>0){
// T temp = list.get(i);
// list.set(i, list.get(j));
// list.set(j, temp);
Collections.swap(list, i, j);
}
}
}
}
}
2.折半,最值,逆序,替換,置換,集合和枚舉的轉換,非同步集合轉同步集合。
折半: 先有序化,然後查找。根據有序化分爲兩種方式:
static <T> int binarySearch(List<? extends Comparable<? super T>> list, T key) 需要自己先排序
使用二分搜索法搜索指定列表,以獲得指定對象。
static <T> int binarySearch(List<? extends T> list, T key, Comparator<? super T> c)
使用二分搜索法搜索指定列表,以獲得指定對象。
最值:
static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) 不需要自己排序
根據元素的自然順序,返回給定 collection 的最大元素。
static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp)
根據指定比較器產生的順序,返回給定 collection 的最大元素。
逆序:
static <T> Comparator<T> reverseOrder()
返回一個比較器,它強行逆轉實現了 Comparable 接口的對象 collection 的自然順序。
static <T> Comparator<T> reverseOrder(Comparator<T> cmp)
返回一個比較器,它強行逆轉指定比較器的順序。
static void reverse(List<?> list)
反轉指定List列表中元素的順序。
例子:TreeSet<String> ts = new TreeSet<String>(Collections.reverseOrder()); //String自己實現了Comparable 接口
原理:TreeSet<String> ts = new TreeSet<String>(new Comparator<String>{
public int compare(String o1,String o2){
int temp = o2.compareTo(o2);
return temp;
}
});
替換:
static <T> boolean replaceAll(List<T> list, T oldVal, T newVal)
使用另一個值替換列表中出現的所有某一指定值。
static <T> void fill(List<? super T> list, T obj)
使用指定元素替換指定列表中的所有元素。
置換:
static void shuffle(List<?> list)
使用默認隨機源對指定列表進行置換。
static void shuffle(List<?> list, Random rnd)
使用指定的隨機源對指定列表進行置換。
集合和枚舉的轉換:
static <T> ArrayList<T> list(Enumeration<T> e)
返回一個數組列表,它按返回順序包含指定枚舉返回的元素。
static <T> Enumeration<T> enumeration(Collection<T> c)
返回一個指定 collection 上的枚舉。
非同步集合轉同步集合:
static <T> List<T> synchronizedList(List<T> list)
返回指定列表支持的同步(線程安全的)列表。
static <T> Set<T> synchronizedSet(Set<T> s)
返回指定 set 支持的同步(線程安全的)set。
static <T> Collection<T> synchronizedCollection(Collection<T> c)
返回指定 collection 支持的同步(線程安全的)collection。
static <K,V> Map<K,V> synchronizedMap(Map<K,V> m)
返回由指定映射支持的同步(線程安全的)映射。
給非同步的集合加鎖 原理:
List list = new ArrayList(); //非同步的
list = MyCollection.synList(list); //返回一個同步的list
class MyCollection{
public List synList(List list){
return new MyList(list);
}
private class MyList implements List{
private List list;
private static final Object lock = new Object();
MyList(List list){
this.list = list;
}
public boolean add(Object obj){
synchronized(lock){
return list.add(obj);
}
}
public boolean remove(Object obj){
synchronized(lock){
return list.remove(obj);
}
}
}
}
Arrays:操作數組的集合工具類
1.折半:包括byte,char,double,float,int,short,long,Object,T泛型數組。
static int binarySearch(byte[] a, byte key)
使用二分搜索法來搜索指定的 byte 型數組,以獲得指定的值。
static int binarySearch(byte[] a, int fromIndex, int toIndex, byte key)
使用二分搜索法來搜索指定的 byte 型數組的範圍,以獲得指定的值。
...
static <T> int binarySearch(T[] a, T key, Comparator<? super T> c)
使用二分搜索法來搜索指定數組,以獲得指定對象。
static <T> int binarySearch(T[] a, int fromIndex, int toIndex, T key, Comparator<? super T> c)
使用二分搜索法來搜索指定數組的範圍,以獲得指定對象。
2.複製指定數組:包括boolean,byte,char,double,float,int,short,long,T泛型數組。
static boolean[] copyOf(boolean[] original, int newLength)
複製指定的數組,截取或用 false 填充(如有必要),以使副本具有指定的長度。
3.複製指定範圍指定數組:包括boolean,byte,char,double,float,int,short,long,T泛型數組。
static boolean[] copyOfRange(boolean[] original, int from, int to)
將指定數組的指定範圍複製到一個新數組。
4.判斷兩個數組是否相等(是指內容全部相同):包括boolean,byte,char,double,float,int,short,long,Object數組。
static boolean equals(boolean[] a, boolean[] a2)
如果兩個指定的 boolean 型數組彼此相等,則返回 true。
5.重置:包括boolean,byte,char,double,float,int,short,long,Object數組。
static void fill(boolean[] a, boolean val)
將指定的 boolean 值分配給指定 boolean 型數組的每個元素。
static void fill(boolean[] a, int fromIndex, int toIndex, boolean val)
將指定的 boolean 值分配給指定 boolean 型數組指定範圍中的每個元素。
6.哈希碼:包括boolean,byte,char,double,float,int,short,long,Object數組。
static int hashCode(boolean[] a)
基於指定數組的內容返回哈希碼。
7.排序:包括boolean,byte,char,double,float,int,short,long,Object,T泛型數組。
static void sort(byte[] a)
對指定的 byte 型數組按數字升序進行排序。
static void sort(byte[] a, int fromIndex, int toIndex)
對指定 byte 型數組的指定範圍按數字升序進行排序。
...
static <T> void sort(T[] a, Comparator<? super T> c)
根據指定比較器產生的順序對指定對象數組進行排序。
static <T> void sort(T[] a, int fromIndex, int toIndex, Comparator<? super T> c)
根據指定比較器產生的順序對指定對象數組的指定範圍進行排序。
8.字符串表示:包括boolean,byte,char,double,float,int,short,long,Object數組。
static String toString(boolean[] a)
返回指定數組內容的字符串表示形式。
原理:
public static void main(String[] args) {
/*
* Arrays:集合框架的工具類。裏面的方法都是靜態的。
*/
int[] arr = {3,1,2,5,6,3};
System.out.println(Arrays.toString(arr));
}
//toString的經典實現。
public static String myToString(int[] a) {
if (a == null)
return "null";
int iMax = a.length - 1;
if (iMax == -1)
return "[]";
StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0; ; i++) { //中間省略判斷,提高了效率
b.append(a[i]);
if (i == iMax)
return b.append(']').toString();
b.append(", ");
}
}
9.其他:
static boolean deepEquals(Object[] a1, Object[] a2)
如果兩個指定數組彼此是深層相等 的,則返回 true。除了比較數組的元素對象,還比較對象的內容。
10.將數組轉成List集合: Arrays-asList方法
static <T> List<T> asList(T... a)
返回一個受指定數組支持的固定大小的列表。
原理:
/*
重點:List asList(數組) 將數組轉成集合。
好處:可以使用集合的方法操作數組中的元素。
注意:
①數組的長度是固定的,所以對於集合的增刪方法是不可以使用的。否則會發生java.lang.UnsupportedOperationException。
②如果數組中的元素是對象,那麼轉成集合時,直接將數組中的元素作爲集合中的元素進行存儲。
如果數組中的元素是基本數據類型數值,那麼會將該數組作爲集合中的元素進行存儲。
int [] arr = {13,25,65};
List<int[]> list = Arrays.asList(arr);
原因:想知道數組是否包含某元素。
想知道元素在數組中的位置。
但是數組集合工具類中沒有這些方法。
*/
public static void main(String[] args) {
String[] arr = {"abc","haha","xixi"};
boolean b = myContains(arr, "haha");
System.out.println(b);
List<String> list = Arrays.asList(arr);//轉換成列表
boolean b1 = list.contains("haha");
System.out.println(b1);
// list.add("a"); //錯誤操作
}
public static boolean myContains(String[] arr, String key){
for (int i = 0; i < arr.length; i++) {
if(arr[i].equals(key))
return true;
}
return false;
}
11.將集合轉成數組: Collection接口-toArray方法
Object[] toArray()
返回包含此 collection 中所有元素的數組。
T[] toArray(T[] a)
返回包含此 collection 中所有元素的數組;返回數組的運行時類型與指定數組的運行時類型相同。
原理:
/*
集合轉換數組:
使用Collection接口中的toArray方法。
好處:可以對集合中的元素操作的方法進行限定。不允許對其進行增刪。
*/
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("abc1");
list.add("abc2");
list.add("abc3");
/*
Object[] toArray()
返回包含此 collection 中所有元素的數組。
*/
Object[] arr1 = list.toArray();
System.out.println(Arrays.toString(arr1));
/*
T[] toArray(T[] a)
返回包含此 collection 中所有元素的數組;返回數組的運行時類型與指定數組的運行時類型相同。
注意:
該方法需要傳入一個指定類型的數組。
如果長度小於集合的size,那麼該方法會創建一個同類型並和集合相同size的數組。
如果長度大於集合的size,那麼該方法會使用指定的數組,存儲集合中的元素,其他位置默認null。
建議,最後長度就指定爲,集合的size.
*/
String[] arr = list.toArray(new String[list.size()]);
System.out.println(Arrays.toString(arr));
}
7.JDK1.5特性
①.JDK1.5給Collection接口找了個父接口Iterable:
實現這個接口允許對象成爲 “foreach” 語句的目標。
foreach語句:底層還是實現的迭代。
格式:
for(類型 變量: Collection集合|數組)
{
}
舉例:
List<String> list = new ArrayList<String>();
list.add("abc1");
list.add("abc2");
list.add("abc3");
/*
Iterator<String> it = list.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
*/
foreach(String s:list){
System.out.println(s);
}
傳統for和高級for的區別:
foreach侷限性:
必須有遍歷的目標(數組或Collection單列集合),一般只用於遍歷,不會對元素進行過多的操作。是用來簡化書寫的。
傳統for循環相對foreach的優勢:
可以定義控制循環的條件和增量。
使用場景:
對數組的遍歷如果僅僅是獲取數組中的元素,可以使用foerach。
如果要對數組的角標進行操作建議使用傳統for。
對於map集合,不能直接使用foreach遍歷。但是可以將map轉換成單列的set,就可以用了。
例子:
public static void main(String[] args) {
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("小米", 2000);
map.put("蘋果", 5000);
map.put("三星", 4000);
//entrySet:
Set<Map.Entry<String, Integer>> entrySet = map.entrySet(); //map變成set集合
for (Map.Entry<String, Integer> entry : entrySet) { //foreach遍歷
System.out.println(entry.getKey() + ":" +entry.getValue());
}
//keySet:
for(String key : map.keySet()){
Integer value = map.get(key);
System.out.println(key + ": " + value);
}
//傳統迭代:
Iterator<Map.Entry<String,Integer>> it= map.entrySet().iterator();
while(it.hasNext()){
Map.Entry<String, Integer> me = it.next();
System.out.println(me.getKey() + ":" + me.getValue());
}
}
2.函數可變參數:int …
內部執行的動作:創建並將參數封裝成數組。
其實就是一個數組,但是接收的是數組的元素。
自動將這些元素封裝成數組。簡化了調用者的書寫。
注意事項:可變參數類型必須定義在參數列表的結尾處。
//求多個正數的和
public static void main(String[] args){
int sum = add(2,5,6,6);
System.out.println("sum= " + sum);
int sum1 = add(2,5,6);
System.out.println("sum1= " + sum1);
}
public static int add(int... arr){ //int... 和int[]等效,簡化書寫。調用傳參的時候不需要建立數組,直接傳值。
int sum = 0;
for(int i:arr)
sum+=i;
return sum;
}
3.靜態導入:導入類的靜態成員,方便程序簡化書寫。
因爲靜態成員不需要對象,直接使用,所以需要在程序加載前進行靜態導入。
import static java.util.Collections.sort;
import static java.util.Collections.max;
或者:
import static java.util.Collections.*;
sort(list);
max(list);
import static java.lang.System.*;
out.println("123"); //out是靜態的,println不是靜態的。out對應一個對象。