首页 > 代码库 > 黑马程序员_集合

黑马程序员_集合

                       

                      

                                  集合
1、集合和对象数组的区别:
  数组的长度不可变,集合的可变;
  数组可以存储基本数据类型和对象,集合只能存储对象。            

                                集合的框架图

                              集合派系的顶层接口Collection
1、Collection集合存储对象的方法:
  add(E e)将元素存储到集合中
  addAll(Collection c)将一个集合添加到另外的集合中
2、Collection集合提取对象的方法:
  通过迭代器iterator中的方法;hasNext()和next()来取出
  

 Iterator it=new iterator();   while(it.hasNext()){    System.out.println(it.next());   }

 


3、Collection集合移除对象的方法:
  boolean remove (Object o)移除一个对象,移除成功返回true。
  boolean removeAll(Collection c)移除两个集合中的共性内容,成功返回true。
4、Collection集合中的其他方法:
  void clear()移除集合中所以元素
  int size()返回集合中元素的个数
  boolean contains(Object o)判断是否包含该对象,包含返回true。
  boolean contains(Collection c)判断一个集合是否包含另一个集合,包含就返回true。
  boolean retainAll(Collection c)将这两个集合的交集赋值给调用者,没有交集就是[]。
  再判断调用者的元素是否改变,改变了就是true。
  Collection中不常用的方法:
  static reverse(List)反转集合
  static fill(List,Object)替换集合
                                        

        

                                          迭代器
1、概念:
  是取出集合中存储对象的方式。

2、原理:  

  在接口Collection中,定义了一个方法 Iterator iterator(),是集合中所有子类都具备的。

  iterator()返回的是一个Iterator接口类型,由于接口本身不能创建对象,所以返回的是接口的实现类对象


3、Iterator接口中的三个方法:
  boolean hasNext()判断集合中还有没有元素,有返回true。
  Object next() 获取集合中的元素,可以看成数组中的arr[i]。
  void remove()移除遍历的集合中的元素
4、实现迭代器的三个步骤:
  //通过集合对象的iterator()方法,返回一个Iterator接口的实现类对象
  

  Iterator it=集合.iterator();  //使用迭代器对象的hasNext()方法,判断集合中还有没有对象可以被取出  while(it.hasNext()){  //使用迭代器对象的next()方法直接获取存在集合中的元素    it.next();  }

 


注:迭代器是获取集合中对象的通用方式。
5、注意事项:
  a.如果迭代器已经获取玩了,再次获取,会出现没有元素被取出的异常。
  b.在迭代的过程中不要使用集合的方法改变集合的长度。
  并发修改异常ConcurrentMOdificationException:
  原因:使用集合的方法修改了集合的长度,而迭代器不知道。
  c:一次迭代中,不能多次出现next()方法,否则可能出现异常,出现数据错乱。
  因为每用一次next(),指针就会往后移一次。
  解决办法:Object o=迭代器.next();



                                            List接口
1、特点:
  有序:取出的顺序和存的时候一样
  具有下标
  允许存储重复元素
2、List集合存对象的方法:
  add(int index,Objext o)index不能超过List集合原有的大小
3、List集合获取对象的方法
  A、一般情况下也是通过iterator()来获取
  但是List派系有特有的迭代器ListIterator,ListIterator接口是继承Iterator的。
  ListIterator接口的特点:
  在迭代的过程中可以对集合中的元素进行添加、修改和删除。
  ListIterator接口的方法:
  add(Object o)
  set(Object o)
  boolean hasPrevious()功能和hasNext()相同
  Object previous()==next()
  B、Object get(int index)获取指定下标的元素
4、List集合修改对象的方法:
  Object remove(int index)
  Object set(int index,Object Obj)
5、List集合其他方法:
  List subList(int start,int end)
注:List集合由于继承关系的存在,也有Collection集合的添加、修改和删除等方法。后面学习
的子类也是这样。



                                      ArrayList类
1、属于List派系下的子类,具备List派系的所有特点:
  有序,下标,重复
  ArrayList自己的特点:
  底层数据结构是数组
  底层是可变数组,导致了查询快,增删慢
  数组的大小默认是10,每次增长50%
  线程不同步,不安全,但执行效率高
注:是通过复制数组的形式实现数组的可变。
2、关于ArrayList集合的面试题
  A、用户通过键盘输入姓名和成绩,姓名和成绩封装成学生对象,存到ArrayList中。
  如果用户输入了"over",结束键盘的输入,迭代集合,迭代出已经存储的学生对象。

 1 public class Person { 2   private String name; 3   private int age; 4  5   public void setName(String name){ 6      this.name=name; 7   } 8   public void setAge(int age){ 9     this.age=age;10   }11   public String getName(){12     return name;13   }14   public int getAge(){15     return age;16   }17 18 19   public Person(){20 21   }22   public Person(String name,int age){23     setName(name);24     setAge(age);25   }26 27   public String toString(){28     return "Person:"+name+"..."+age;29   }30 }31 import java.util.*;32 public class ArrayListTest {33 public static void main(String[] args) {34   //创建一个集合,创建一个键盘输入的Scanner35   ArrayList array = new ArrayList();36   Scanner sc = new Scanner(System.in);37   //接受键盘输入38   while(true){39     String str = sc.nextLine().trim();40     //判断输入的是不是over 41   if("over".equals(str))42     break;43   //进行字符串的切割操作,空格的形式44   String[] s = str.split(" +");45   //姓名和成绩存储到ArrayList集合,成绩转成int46   array.add(new Student(s[0],Integer.parseInt(s[1])));47   }48   //创建迭代器,获取集合中的元素49   Iterator it = array.iterator();50   while(it.hasNext()){51     System.out.println(it.next());52   }53  }54 }

 

B、制作一个6位的不同字符的验证码,输出到控制台,用户输入,判断是对还是错。
要求:6位不同的数子,字母和汉字。

import java.util.*;public class GetCode{  public static void main(String[] args){    Scanner sc=new Scanner(System.in);    //定义数组,保存一些字符    char[] ch={‘a‘,‘v‘,‘1‘,‘3‘,‘6‘,‘你‘,‘黑‘,‘我‘,‘为‘,‘的‘,‘啊‘};    //创建一个字符串缓冲区    while(true){    StringBuilder sb=new StringBuilder();    //因为只需6个字符,所以sb的长度为6就停止添加字符    while(sb.length()<6){    int num=new    Random().nextInt(ch.length);    if(sb.toString().contains(ch[num]+""))      continue;      sb.append(ch[num]);    }    System.out.println(sb);    //再比较字符串与输入的是否相等。相等就跳出循环,不等就刷新验证码。    String code=sc.nextLine();    if(sb.toString().equals(code)){    System.out.println("验证码正确!");        break;    }else{      System.out.println("验证码错误!");    }  } }}

 


3、去掉ArrayList中的重复元素
要求ArrayList集合中对象重写equals()方法。

/*方法中返回一个新集合替换老集合*/public static ArrayList method(ArrayList oldArray){//定义新集合,存储去掉重复元素后的集合  ArrayList newArray=new ArrayList();  Iterator it=oldArray.iterator();  while(it.hasNext()){  Object obj=it.next();  if(!newArray.contains(obj))    newArray.add(obj);  }  return newArray; }contains()会调用equals();对象中重写的equals():public boolean void equals(Object obj){  if(this==obj)    return true;  if(obj==null)    return false;  if(obj instanceof Person){    Person p=(Person)obj;    return this.name.equals(p.name)&&this.age==p.age;  }}

 


                                        Vector类
没有集合框架以前,存储对象只能依赖Vector。Vector底层的数据结构也是可变数组,
但是它线程安全,运行效率慢,而且每次数组扩容是100%。所以被ArrayList取代了。



                                          LinkedList类
1、特点:
  底层数据结构是链表实现,以记录对象地址的方式存储
  查询慢,增删快
  线程不安全,执行效率高
2、LinkedList类特有的方法
  addFirst()将元素添加到链表的开头
  addLast()将元素添加到链表的结尾
  Object getFirst()获取链表的开头
  Object getLast()获取链表的结尾
  Object removeFirst()移除链表的开头,返回被移除前的对象
  Object removeLast()移除链表的结尾, 返回被移除前的对象

JDK1.6开始,有n个新的方法,把以前的方法取代,不做掌握
  offerFirst()>addLast()
  peekFirst()>getLast()
  pollFirst()>removeLast()

3、LinkedList模拟堆栈和队列,体现的是思想的转换:将jdk中的方法封装起来,让用户调用我们自己的方法来执行程序,而不是
用jdk中的方法。

import java.util.*;class MyMethod{  private LinkedList link=null;  public MyMethod(){  link=new LinkedList();}  public void add(Object obj){    link.add(obj);  }  //先进先出  public Object get(){    return link.removeFirst();  }  //先进后出  public Object get(int x){    return link.removeLast();  }  public boolean isEmpty(){    return !link.isEmpty();  } }public class LinkedListOut{  public static void main(String[] args)   {    MyMethod my=new MyMethod();    my.add("2");    my.add("3");    my.add("4");    while(my.isEmpty()){      System.out.println(my.get(2));    }  }}

 


                                     Set 接口
1、Set集合派系的特点:
  不允许存储重复元素
  没有下标
  无序的集合,存储的和取出的顺序不一样
2、Set接口中的方法和Collection接口中的一致

 

                                        HashSet类
1、HashSet类除有和Set接口一样的特点外,自身的特点是:
  底层数据结构:哈希表,存储的是对象的哈希值
  HashSet本身没有功能,功能来自于HashMap。HashSet调用HashMap的功能。
2、HashSet的存储原理:
  存储对象时,先调用对象的hashcode(),获取该对象的哈希值(十进制数)。HashSet看
  存储过这个哈希值没。如果没有就将这个哈希值存到自己的哈希表中;如果有,就再
  调用对象的equals()。如果相等就不存到HashSet集合中,不等就挂载到HashSet中原
  有的哈希值下面。
3、HashSet集合取出重复的元素,保证对象的唯一性
    需重写hashCode()和equals():
  

    public int hashCode(){      //利用name变量 age变量      return    name.hashCode()+age*13;    }    //同名的,和同年龄的对象,返回真    public boolean equals(Object obj){      if( this == obj)        return true;      if(obj == null)        return false;      if(obj instanceof Person){        Person p = (Person)obj;        return this.name.equals(p.name) && this.age == p.age;      }       return false;    }

 


4、hashCode和equals的问题
如果两个对象具有相同的哈希值,两个对象进行equals比较,不一定返回真;
如果两个对象进行equals比较,返回真,两个对象具有相同的哈希值吗,必须相同。


                                LinkedHashSet类---HashSet的子类
LinkedListSet独有的特点:
  有序的,基于哈希表的链表,线程不安全,执行效率高

 


                                            TreeSet类
1、TreeSet独有的特点:
  底层结构是二叉树;
  对存储的对象自然排序;
  线程不安全,执行效率高。
2、将自定义的对象Person存储到TreeSet集合,出现了类型转换异常
  ClassCastException :原因,Person不能被转成java.lang.Comparable
  TreeSet中,需要对Person进行排序,可是你的Person对象,不具备自然顺序。
3、TreeSet集合中使对象具备自然顺序:
  自定义的类需实现Comparable接口,重写compareTo()方法;
  或者,自定义比较器类,实现接口Comparator,重写Compare(Object o1,Object o2)。

 1   方法一: 2   //主要比较姓名,如果姓名一样,比较年龄。也可以主要比较年龄 3   public int compareTo(Object obj){ 4     //this 后进来的, obj先进来的对象 5     Person p = (Person)obj; 6     int num = this.name.compareTo(p.name); 7     return num == 0 ? this.age - p.age : num; 8   } 9   方法二:10   /*11   * 自定义比较器,对于TreeSet集合中存储的Person对象进行排序12   */13   Set set=new TreeSet(new Comparator());14   public class MyComparator implements java.util.Comparator{15     //重写compare方法,做什么事情,比较Person对象的年龄和姓名16     //    ts.add(new Person("lisi",20));17     // ts.add(new Person("zisi",21));18     public int compare(Object o1, Object o2){19     //o1 和 o2 比较一下,年龄,主要条件,年龄不同,不需要比较姓名20       Person p1 = (Person)o1;21       Person p2 = (Person)o2;22       //Person的变量,只能用get方法获取23       int num = p1.getAge() - p2.getAge();24       return num == 0? p1.getName().compareTo(p2.getName()) : num;25     }26 27   }

 

注:两个方法都是大于0,就存后面;等于0,就不存;小于0,存前面。

 

 

                                        泛型
1、格式:
  集合类<数据类型> 变量 = new 集合类<数据类型>();
  数据类型,指定了这个集合,只能存储这个类型的对象。
  如: ArrayList<E> boolean add(E e) E:看成是一个变量
  ArrayList<String> 所有的E都变成了String
  注意:存储基本数据类型,泛型必须写对应的包装类
2、泛型的好处:
  泛型的出现,将安全问题,由运行时期,提前到了编译时期
  减少了代码,使用泛型避免类型的强制转换
3、自定义的泛型,保证数据安全
  泛型类,声明类的时候,加上泛型
  泛型方法, 在方法上定义泛型,和类无关,需声明;
  public<T> void method(T t){}
  public static<T> void method(){}
注意:   静态的方法的泛型不要和类上的一样。因为
    静态的成员是优先于对象存在,用类名调用时无法指定
    泛型的类型。
    方法中的泛型是局部的泛型,不影响类名的泛型。
    泛型接口,在接口名后面加上
    通常定义实现类,实现接口的时候,不指定泛型,等待new实现类对象
    的时候指定。
class MyComparator <T> implements Comparator<T>(){}
4、泛型的通配符和限定
  上限限定 ? extends E 传递E类型,E的任意子类类型
  下限限定 ? super E 传递E类型,E的任意父类类型
  弊端:使用通配符后,不能强转。

 




                                           Map接口
1、Map接口的特点:
  一个键最多只能映射一个值
  不允许出现重复的键
2、Map接口的方法:
  V put(K,V) 将键值对存储到集合。返回值,存储了重复的键,返回被覆盖之前的值
  V get(K) 根据键获取值,传递一个键,返回键映射的值,没有这个键,返回null
  Set<K> keySet()键存储到Set集合
  Set<Map.Entry<K,V>> entrySet()2014/8/15映射关系对象保存到Set集合
  V remove(K)移除指定的键对应的值,返回被移除前的值,没有移除成功,返回null
  boolean containsKey(K)判断集合中,有没有这个键,有返回true
  boolean containsValue(V)判断集合中个,有没有这个值,有返回true
  Collection values()将集合中的所有值,保存到Collection集合
3、获取Map集合中键值对的方式?
  第一种,利用Map中的一个方法keySet(),Map集合中的键,存储到Set集合
  迭代Set集合,获取出来的是所有的键
  通过键获取值,Map接口中的get方法
 

1   Map<String,Integer> map=new HaspMap<String,Integer>();2   Set<String> set=map.keySet();3   Iterator<String> it=set.iterator();4   while(it.hasNext()){5     String key=it.next();6     Integer value=http://www.mamicode.com/map.get(key);7     System.out.println(key+"..."+value);8   }

 

第二种利用Map集合中的键值对映射关系获取
Map接口中有一个方法entrySet(),获取键值对的映射关系对象Entry,将这个对象Entry存储到了Set集合
迭代Set集合,获取出来的Set集合中存储的是映射关系对象Entry
通过映射关系对象中的方法 getKey getValue

1 Map<String,Integer> map=new HasMap<String,Integer>();2 Set<Map.Entry<String,Integer>> set=map.entrySet();3 Iterator <Map.Entry<String,Integer>> it=map.iterator();4 while(it.hasNext()){5   Map.Entry<String,Integer> me=it.next();6   String key=me.getKey();7   Integer value=http://www.mamicode.com/me.getValue();8  System.out.println(key+"..."+value);9 }

 

 


                                        HashMap类
1、HashMap独有的特点
  底层结构是哈希表,允许存储null值,null键
  不同步,线程不安全,执行效率高
2、保证存储到的HashMap中的键唯一性。自定义对象保证唯一性,自定义对象需重写hoseCode()和equals()。

                                      LinkedHashMap类-------HashMap的子类

1、特点:
  底层结构是基于哈希表的链表实现的
  键怎么存,怎么取
  不同步,线程不安全,执行效率高


                                            TreeMap类
1、TreeMap的特点:
    底层数据结构是红黑树,记自然平衡二叉树
    对键进行自然的排序,要求作为键的对象,具备自然排序或者自定义比较器
    不同步,线程不安全,执行效率高

2、TreeMap使用方式和TreeSet是一致的,保证对象的自然顺序,或者定义比较器就可以了。在存储的时候,Set使用add,Map使用put。

 


                                      HashTable类

1、HashTable的特点:
  底层数据结构式哈希表
  线程安全,不允许存null值,null键
  因为HashTable出了线程和null的区别,其他的和HashMap的一致,所以被HashMap取代了。但是HashTable的一个子类Properties至今
  很活跃。

 

                                    Properties类---HashTable的子类
1、Properties的特点:
  线程安全,与IO流配合使用。这个集合的泛型已经定好了,键值的泛型都是String。
2、Properties的两个特有的方法:
  setProperty(String key,String value)
  getProperty(String key)

3、计算一个字符串中每个字符出现的次数:

 1 import java.util.*; 2 public class TreeMapTest { 3 public static void main(String[] args) { 4 String s = "asrrfegrr4gw34g"; 5 //String s = "aa"; 6 //建立TreeMap集合,键就是单个字符,值出现次数 7 TreeMap<Character,Integer> tm = new TreeMap<Character, Integer>(); 8 //将字符串变成字符数组,操作单个字符 9 char[] ch = s.toCharArray();10 for(int x = 0 ; x < ch.length ; x++){11 //遍历到每一个字符,字符当作键,到集合中获取值12 Integer i = tm.get(ch[x]);13 if(i==null){14 //说明键,不存在的,字符没有出现过,把字符当作键,值为1存储到集合15 tm.put(ch[x], 1);16 }else{17 //说明键存在,将值++后存储会集合18 19 tm.put(ch[x], ++i);20 }21 }22 System.out.println(tm);23 }24 }

 

                                          JDK1.5后的新特性
1、增强for循环:只适合遍历数组和集合
  格式:
  for(数据类型 变量:集合或者数组){
    输出(变量);
  }
2、可变参数格式:
  方法名(数据类型...变量名)
  注意事项:
    可变参数的数据类型唯一
    多个参数,可变参数放最后
    可变参数只能写一个

 


                                 Collections集合工具类<List专用>
1、凡是工具类里面的成员都是静态的
2、集合工具类的方法:
  static sort(List list)可以排序List集合,升序
  static sort(List list,Comparator com)传递List,比较器,按照比较器对集合升序排序
  static Comparator reverseOrder()返回一个比较器,逆转了对象的自然顺序(Comparable)
  static Comparator reverseOrder(传递比较器)返回比较器,逆转了传递的比较器的顺序
  static int binarySerach(List list,Object key)集合的折半查找,如果找不到该元素,返回-插入点-1。插入点,就是将查 找的元素,放在集合中个,保证集合有序, 下标插入点
  static reverse(List list)反转集合
  static fill(List list,Object o)替换集合中的所有对象
  static shuffle(List list)随机排列集合中的对象,洗牌
  Collections工具类中有一组方法 synchronized开头的,将线程不安全的集合,变成线程安全的集合

 

 

                                              Arrays数组工具类
数组工具类中的方法:
  static sort(数组) 数组进行升序排序,快排
  static int binarySerach(数组,查找的元素)数组的折半查找
  static String toString(数组)不是重写Object,将数组变成字符串
  static List asList(数组)数组转集合,List集合,集合长度不可变
  2Collection接口方法 T[] toArray(T[] a) 集合变数组