集合类

 黑马程序员

---------------------- <a href="http://edu.csdn.net/heima" target="blank">android培训</a>、<a href="http://edu.csdn.net/heima" target="blank">java培训</a>、期待与您交流! ----------------------

 

1.1.   集合概念

       为什么出现集合类?

    在面向对象的编程思想中,都是以对象的形式对事物进行描述的,为了保证对象的生命周期,我们需要持有对象

    在很多情况下,我们不知道在程序中需要创建多少个对象,这时就不能依靠定义引用对象的变量来持有每一个对象

    存储对象的容器就能帮我们解决这样的问题,而集合便是这样的容器

         数组和集合类的区别

    数组和集合类都是容器,都能存储对象

    集合类的优势就在于长度可变

        集合类的特点

    集合类可用于存储对象

    集合类的长度可变

    一个集合可以存储多种类型的对象

Collection定义了集合框架的共性功能。
 添加
 add(e);
 addAll(collection);

 

 删除
 remove(e);
 removeAll(collection);
 clear();

package cn.itcast.day12.list;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import cn.itcast.day12.domain.Person;

public class RemoveDemo {

 public static void main(String[] args) {
//  test1();
//  test2();
//  test3();
 }

 private static void test3() {
  List<Person> list = new ArrayList<Person>();
  list.add(new Person("沙 ", 22));
  list.add(new Person(" 成龙", 23));
  list.add(new Person("李连杰", 22));
  list.add(new Person("高 ", 22));
  
  for (Person p : list)
   if(p.getAge() == 22)  // 增强for循环迭代时, 不能修改
    list.remove(p);
 }

 private static void test2() {
  List<Person> list = new ArrayList<Person>();
  list.add(new Person("沙 ", 22));
  list.add(new Person(" 成龙", 23));
  list.add(new Person("李连杰", 22));
  list.add(new Person("高 ", 22));
  
  Iterator<Person> iter = list.iterator();
  while(iter.hasNext())
   if(iter.next().getAge() == 22)
    iter.remove();   // 使用迭代器迭代时, 要用迭代器的remove()而不是List的remove()
  
  System.out.println(list);
 }

 private static void test1() {
  List<Person> list = new ArrayList<Person>();
  list.add(new Person("沙 ", 22));
  list.add(new Person(" 成龙", 23));
  list.add(new Person("李连杰", 22));
  list.add(new Person("高 ", 22));

  // 删除该集合中年龄等于22岁的Person
  for (int i = 0; i < list.size(); i++)
   if (list.get(i).getAge() == 22)
    list.remove(i--);  // 使用普通for循环迭代时, 删除元素后要将循环变量减1
  
  System.out.println(list);
 }

}

 


 判断。
 contains(e);
 isEmpty();
 

获取
 iterator();
 size();
 

获取交集。
 retainAll();

 集合变数组。
 toArray();

package cn.itcast.day12;

import java.util.ArrayList;

import cn.itcast.day12.domain.Person;

public class Test {

 public static void main(String[] args) {
  //  test1();
  //  test2();

 }

 private static void test2() {
  ArrayList list = new ArrayList(); // List容器, 长度可变, 可存储任意类型对象
  list.add(new Person("沙", 23));
  list.add(new Person("王", 22));
  list.add(new Person("高", 23));
  list.add(new Person("邢", 24));
  list.add(new Person("xxx", 24));
  list.add(new Person("ooo", 24));
  list.add(123);  // 基本数据类型装入集合的时候进行了自动装箱的操作. int --> Integer 
  list.add('a');  // char --> Character
  list.add(true);  // boolean --> Boolean
  list.add("字符串");
  
  for (int i = 0; i < list.size(); i++)
   System.out.println(list.get(i));
 }

 private static void test1() {
  Person[] arr = new Person[3]; // 数组容器, 长度不可变, 类型一致
  arr[0] = new Person("沙", 23);
  arr[1] = new Person("王", 22);
  arr[2] = new Person("高", 23);
  // arr[3] = new Person("邢", 24); // ArrayIndexOutOfBoundsException
  // arr[0] = 123;
  
  
  for (int i = 0; i < arr.length; i++)
   System.out.println(arr[i]);
 }

}

 

1.2.   集合接口

         Collection接口

   一个独立的元素的序列,这些元素服从一条或多条规则

    Collection接口下主要分为List集合和Set集合

    List集合的特点是元素有序、允许有重复元素

    Set集合的特点是元素无存储顺序、不允许有重复元素

         Map接口

    一组成对的”键值对”对象,允许根据键来查找值

    Map集合的键不允许有重复,所以Map的所有键构成了一个Set集合

    主要学习HashMap和TreeMap

         Iterable接口

   JDK1.5新定义的接口作为Collection的父接口

    主要为了实现增强for循环

package cn.itcast.day12.list;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Vector;

import cn.itcast.day12.domain.Person;

public class ListIteration {

 public static void main(String[] args) {
  LinkedList list = new LinkedList();
  
  list.add(new Person("沙", 22));
  list.add(new Person("成龙", 23));
  list.add(new Person("李连杰", 24));
  list.add(new Person("李连杰", 50));
  
  //  iter1(list);
  //  iter2(list);
  //  iter3(list);
  
  iter4(list);
 }

 private static void iter4(List list) {
  for(Object obj : list)    // 增强for循环, JDK5之后新增. for(类型 变量名 : 容器)
   System.out.println(obj);
 }

 // 此种方式是Vector中特有的
 private static void iter3(Vector list) {
  Enumeration e = list.elements();   // 获取枚举
  while(e.hasMoreElements())     // 判断是否有下一个元素
   System.out.println(e.nextElement()); // 获取下一个元素
 }

 private static void iter2(List list) {
  Iterator iter = list.iterator();  // 获取迭代器对象
  while(iter.hasNext())     // 判断是否有下一个元素
   System.out.println(iter.next()); // 获取下一个元素
 }

 private static void iter1(List list) {
  for(int i = 0; i < list.size(); i++)
   System.out.println(list.get(i));
 }

}

 

 

 

1.3.   List

         List特点

    元素有序,可重复。

    我们主要学习三种:ArrayList、Vector、LinkedList

    这三种都是List接口的实现类,使用上完全一样,只是实现原理不同,效率不同。

         ArrayList

    底层数组实现

    查找快,增删慢

    线程不安全

       LinkedList

    底层链表实现

    增删块,查找慢


import java.util.*;
class DuiLie
{
 private LinkedList link;

 DuiLie()
 {
  link = new LinkedList();
 }
 
 public void myAdd(Object obj)
 {
  link.addFirst(obj);
 }
 public Object myGet()
 {
  return link.removeFirst();
 }
 public boolean isNull()
 {
  return link.isEmpty();
 }

}

 

class  LinkedListTest
{
 public static void main(String[] args)
 {
  DuiLie dl = new DuiLie();
  dl.myAdd("java01");
  dl.myAdd("java02");
  dl.myAdd("java03");
  dl.myAdd("java04");

  while(!dl.isNull())
  {
   System.out.println(dl.myGet());
  }
 }
}

 

        Vector

    与ArrayList基本一样

    线程安全(线程同步),效率低


import java.util.*;

/*
枚举就是Vector特有的取出方式。
发现枚举和迭代器很像。
其实枚举和迭代是一样的。

因为枚举的名称以及方法的名称都过长。
所以被迭代器取代了。
枚举郁郁而终了。

 

*/
class VectorDemo
{
 public static void main(String[] args)
 {
  Vector v = new Vector();

  v.add("java01");
  v.add("java02");
  v.add("java03");
  v.add("java04");

  Enumeration en = v.elements();

  while(en.hasMoreElements())
  {
   System.out.println(en.nextElement());
  }
 }
}

 

         存取元素

    List集合元素存取方法一致

    使用add()方法增加元素

    由于List集合有序,可以使用get()方法获取元素

 

package cn.itcast.day12.exercise;

import java.util.ArrayList;
import java.util.List;
import java.util.TreeSet;

import cn.itcast.day12.domain.Person;

/*
 * 将List集合中的对象按照自然顺序排序
 */
public class Test1 {

 public static void main(String[] args) {
  List<Person> list = new ArrayList<Person>();
  list.add(new Person("zlt", 21));
  list.add(new Person("ssb", 22));
  list.add(new Person("ycl", 23));
  list.add(new Person("llj", 20));
  list.add(new Person("gx", 21));
  list.add(new Person("lk", 22));
  list.add(new Person("lk", 21));

  bubbleSort(list);

  System.out.println(list);

 }

 @SuppressWarnings("unchecked")
 private static void bubbleSort(List list) {
  for (int i = 0; i < list.size() - 1; i++)
   for (int j = 0; j < list.size() - 1 - i; j++) {
    Comparable c1 = (Comparable) list.get(j);
    Comparable c2 = (Comparable) list.get(j + 1);
    if (c1.compareTo(c2) > 0) {
     Object temp = list.get(j);
     list.set(j, list.get(j + 1));
     list.set(j + 1, temp);
    }
   }

 }

 private static void bubbleSort(int[] arr) {
  for (int i = 0; i < arr.length - 1; i++)
   for (int j = 0; j < arr.length - 1 - i; j++)
    if (arr[j] > arr[j + 1]) {
     int temp = arr[j];
     arr[j] = arr[j + 1];
     arr[j + 1] = temp;
    }
 }

 private static <T> void sort(List<T> list) {
  // 将list中的所有元素放入TreeSet, 其中可以自动排序
  TreeSet<T> set = new TreeSet<T>(list);
  // 将List清空
  list.clear();
  // 再将TreeSet中的元素放回List
  list.addAll(set);
 }

}

 

    元素的迭代(Iterator)

通过集合对象的iterator()方法获得迭代器Iterator

通过Iterator迭代器的hasNext()方法判断是否存在下一个元素

通过Iterator迭代器的next()方法获取下一个元素

   元素的迭代(Enumeration)

迭代Vector集合中的元素可以使用Enumeration

通过Enumeration的hasMoreElements()方法判断是否还有元素

通过Enumeration的nextElement()方法返回下一个元素

 

package cn.itcast.day12.list;

public class MyArrayList {
 private Object[] data = new Object[10];
 private int size;
 
 /*
  * 添加对象
  *   检查数组是否装满, 如果装满则创建新数组, 如果没装满直接给数组元素赋值
  *    创建一个更大的新数组, 将原有数据复制到新数组
  *   将添加的对象放入数组, 第一次放0号, 第二次放1号, 以此类推
  */
 public void add(Object obj){
  if(size == data.length){           // 如果装满了
   Object[] newArr  =  new Object[data.length * 3 / 2 + 1];  // 创建新数组
   System.arraycopy(data, 0, newArr, 0, size);      // 将原数组中数据拷贝到新数组
   data = newArr;             // 记住新数组
  }
  data[++size] = obj;   
 }
 
 /*
  * 获取对象
  *   将数组中指定索引上的元素返回
  */
 public Object get(int index){
  checkRange(index);  
  return data[index];
 }
 
 /*
  * 删除对象
  *   拷贝数组, 从当前数组要删除的元素的下一个位置开始, 拷贝到当前数组要删除的位置, 拷贝 size - index - 1 个
  *   将数组最后一个元素赋值为null
  */
 public void remove(int index){
  checkRange(index);
  System.arraycopy(data, index + 1, data, index , size - index - 1);
  data[--size] = null;
 }
 
 /*
  * 修改对象
  */
 public void set(int index, Object obj){
  checkRange(index);
  data[index] = obj;
 }
 
 /*
  * 检查索引是否越界, 如越界抛出异常给予提示
  */
 public void checkRange(int index){
  if(index >= size || index < 0)
   throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
 }
 
 public int size(){
  return size;
 }
}

 

 

1.4.   JDK5新特性

        泛型

    由于集合可以存储不同类型的数据,所以取元素时有可能会导致类型转换错误

    JDK1.5增加了新特性泛型,为了减少操作集合时出错的几率

   集合一旦声明了泛型,便只能存储同一类型的对象了

    使用方法:ArrayList<Person> al = new ArrayList<Person>();

    使用泛型的好处

提高了程序的安全性

将运行期遇到的问题转移到了编译期

省去了类型强转的麻烦

泛型类的出现优化了程序设计

       增强for循环

    新接口Iterable中定义了增强for循环

    可以通过增强for循环对数组和集合进行遍历

    语法:for(类型 变量名 :  要遍历的容器) { …… }

          可变参数

    有的时候在设计方法时无法确定将来别人会传入的参数个数

    JDK1.5增加了新特性可变参数,在函数中只声明参数类型,不规定个数

    方法接受的参数实际上是一个数组,可以在方法中遍历数组

    可变参数只能被定义为函数的最后一个形参

    语法格式: 返回值 函数名(参数类型… 形参名)

 

package cn.itcast.day12.jdk5;

public class VariableParameter {

 public static void main(String[] args) {
  print("abc", "xxx", 1, 2, 3, 4);
 }

 /*
  * 类型... 变量名
  * 可变参数, 可以接收该类型的实参0个, 1个, 或多个, 或者一个数组
  * 之后会将所有实参装入一个指定类型的数组
  * 可变参数只能放在参数列表的最后一个
  */
 public static void print(String s1, String s2, int... arr) {
  for (int i : arr)
   System.out.println(i);
 }

 /*
 public static void print() {
 }

 public static void print(int i) {
  System.out.println(i);
 }

 public static void print(int i, int j) {
  System.out.println(i);
  System.out.println(j);
 }

 public static void print(int i, int j, int k) {
  System.out.println(i);
  System.out.println(j);
  System.out.println(k);
 }

 public static void print(int[] arr) {
  for (int i : arr)
   System.out.println(i);
 }
 */
}

 

 

 

泛型:JDK1.5出现的特性,是一个类型安全机制。

好处:
 将运行时期的问题ClassCastException转移到了编译时期。
 避免了强制转换的麻烦。

格式:<>

将不确定的类型提供形式参数。

让使用者,来明确具体操作的类型。将具备作为实际参数传递进来即可。

//泛型类。
class Tool<T>
{
 public void show(T t)
 {
  System.out.println(t);
 }

 //方法泛型。
 public <Q> void show2(Q q)
 {
  
 }
}


class Tool
{
 public void show(Object obj)
 {}

 public void show2(Object obj)
 {}
}

 

泛型的高级特性,类型限定。

? :不确定类型。可以视为是一个占位符。
? extends E : 可以接收E类型或者E子类型。
?super E :可以接收E类型或者E的父类型。

 */

class Student implements Comparable
{

 //让学生对象具备自然顺序。
 public int compareTo(Object obj)
 {
  
 }

 //定义学生的哈希值。
 public int hashCode()
 {
 
 }
 //定义判断学生对象是否相同。
 public boolean equals(Student obj)
 {
  
 }
 //定义学生对象的字符串表现形式。
 public String toString()
 {
  
 }
}

class 
{
 public static void main(String[] args)
 {

  Tool<String> t = new Tool<String>();
  t.show("4");

  ArrayList<Integer> al = new ArrayList<Integer>();

  Iterator<Integer> it = al.iterator();

 }
 public static void printColl(Collection<? extends Person> coll)
 {
  
 }
}

class Person
{
 
}
class Worker extends Person
{
}
class Teacher extends Person
{
}

/*
class WComp implements  Comparator<Worker>
{
 public int compare(Worker w1,Worker w2)
 {}
}

class TComp implements  Comparator<Teacher>
{
 public int compare(Teacher w1,Teacher w2)
 {}
}
*/

/*
TreeSet(Collection<? extends E> c)
          构造一个包含指定 collection 元素的新 TreeSet,它按照其元素的自然顺序进行排序。
TreeSet(Comparator<? super E> comparator)
 

ArrayList<Worker> al = new ArrayList<Worker>();
al.add(new Worker());


TreeSet<Person> ts = new TreeSet<Person>(al);


Iterator<Person> it = ts.iterator();

Person p = it.next();

 

class PersonComp implements Comparator<Person>// ? super Worker
{
 public int compare(Person p1,Person p2)
 {}
}

TreeSet<Worker> ts = new TreeSet<Worker>(new PersonComp());
ts.add(new Worker());


TreeSet<Teacher> ts = new TreeSet<Teacher>(new PersonComp());
ts.add(new Teacher());

*/

 

1.5.   Set

         Set集合无序,不允许有重复元素

    Set集合通过存入对象的equals方法来保证集合中没有重复元素

       HashSet

   HashSet是Set的子类,因此也没有重复元素

   底层使用哈希算法保证没有重复元素

    存储对象时,先调用对象的hashCode()方法计算一个哈希值,在集合中查找是否有哈希值相同的对象。

如果没有哈希值相同的对象,直接存入。

如果有哈希值相同的对象,则和哈希值相同的对象进行equals()方法比较。

equals()方法比较结果相同则不存,不同就存入。

    往HashSet集合里存储的对象必须正确重写hashCode和equals方法

   HashSet存储元素效率非常高

 


 

       TreeSet

   TreeSet集合通过二叉树算法保证无重复元素,并对元素进行排序

    在使用TreeSet时必须指定比较的算法,指定的方式有两种:

自然顺序:将要存储的类实现Comparable接口,重写compareTo方法,在方法中指定算法

比较器顺序:在创建TreeSet时,传入一个比较器Comparator,在比较器的compare方法中指定算法

 


import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;

import cn.itcast.day12.domain.Person;

/*
 * 在使用TreeSet的时候必须指定比较算法
 *   自然顺序: 在要存储的对象的类上实现Comparable接口, 重写Comparable接口中的compareTo()方法
 *   比较器顺序: 在构造函数中传入一个Comparator接口的子类对象, 那么TreeSet在存储对象的时候就会调用这个对象的compare方法
 *
 * 如果两种方式都存在, 那么优先比较器. 为什么?
 *   如果优先比较器, 我们可以定义多个比较器按照不同的顺序比较, 如果优先自然顺序, 那么只能按照一种顺序比较.
 *   
 */
public class TreeSetDemo {

 public static void main(String[] args) {
  Set<Person> set = new TreeSet<Person>(new MyComparator());
  
  set.add(new Person("zlt", 21));   
  set.add(new Person("ssb", 22));  
  set.add(new Person("ycl", 23));  
  set.add(new Person("llj", 20));  
  set.add(new Person("gx", 21));  
  set.add(new Person("lk", 22));   
  set.add(new Person("lk", 21));   // Set集合重复元素不存.
  
  for (Person p : set)
   System.out.println(p);
 }

}

class MyComparator implements Comparator<Person>{  // 定义比较器实现Comparator接口
 public int compare(Person p1, Person p2) {   // 重写compare
  int nameGap = p1.getName().compareTo(p2.getName());
  return  nameGap != 0 ? nameGap : p1.getAge() - p2.getAge(); 
 }
}

 

 

1.6.   Map

         Map集合的特点

   Map存储了一系列键值的映射关系

    Map集合需要保证键的唯一性

   可以通过键获得值,反之则不能

  Map集合存储元素使用put(key,value)方法

      Map集合的两种遍历方式

 通过keySet方法返回由键组成的集合,迭代该集合的元素就拿到了所有的键,再调用get方法根据键拿到值

   通过entrySet方法返回键值映射关系组成的集合,迭代该集合就拿到了一个个的键值映射关系,通过getKey方法拿到键,通过getValue方法拿到值。

        HashMap

   线程不安全,存取速度快,允许存放null键,null值。

    通过HashSet原理保证键唯一性

 

/*练习一:

描述学生对象:属性name,age。方法,getName,getAge,这是基本方法。

每一个学生都有自己的归属地,归属地用字符串表示。

将学生对象和归属地存入集合。

取出所有信息,用两种方式取出。
注意。刻意存入姓名和年龄都相同的学生。如果保证学生的唯一性。

HashMap<Student,String> hm= new HashMap<Student,String>();
hm.put(new Student("lisi",20),"beijing");
hm.put(new Student("lisi",20),"shanghai");

练习二:
基于上一个练习的对象的定义。

对学生对象的年龄进行排序。提示:使用TreeMap。
然后在不改动年龄排序的情况下,按照姓名排序。

*/


import java.util.*;


class Student
{
 private int age;
 private String name;
 Student(String name,int age)
 {
  this.name = name;
  this.age = age;
 }

 public int hashCode()
 {

  final int NUMBER = 38;

  return name.hashCode()+age*NUMBER;
 }
 public boolean equals(Object obj)
 {
  if(!(obj instanceof Student))
   throw new ClassCastException("不是学生对象");

  Student stu = (Student)obj;

  return this.name.equals(stu.name) && this.age == stu.age;
 }
 public int getAge()
 {
  return age;
 }
 public String getName()
 {
  return name;
 }
}


class  HashMapTest
{
 public static void main(String[] args)
 {
  HashMap<Student,String> hm = new HashMap<Student,String>();

  hm.put(new Student("lisi02",20),"beijing");
  hm.put(new Student("lisi22",22),"shanghai");
  hm.put(new Student("lisi32",23),"nanjing");
  hm.put(new Student("lisi12",21),"shenzhen");
  hm.put(new Student("lisi12",21),"铁岭");


  //第一种keySet.

  Set<Student> keySet = hm.keySet();

  Iterator<Student> it = keySet.iterator();

  while(it.hasNext())
  {
   Student stu = it.next();
   String addr = hm.get(stu);

   System.out.println(stu.getName()+"...."+stu.getAge()+"...."+addr);
  }
  /*
  //第二种
  Set<Map.Entry<Student,String>> entrySet = hm.entrySet();

  for(Iterator<Map.Entry<Student,String>> it1 = entrySet.iterator(); it1.hasNext(); )
  {
   Map.Entry<Student,String> me = it1.next();
   Student stu = me.getKey();
   System.out.println(stu.getName()+":::"+stu.getAge()+"::::"+me.getValue());
  }
  */

 }
}

 

 

       Hashtable

   线程安全,速度慢,不允许存放null键,null值,已被HashMap替代。

        TreeMap

    通过二叉树算法保证键唯一性

    对键进行排序,排序原理与TreeSet相同。

         Properties

    HashTable的子类,所以也是线程安全的

    用于读写配置文件的,一般配置项等号两边都是String,所以该集合中的两列保存的都是String类型的数据

    这个集合中只能存String,所以不需要定义泛型。

 

Map:存储一对元素,键值对。必须要保证键的唯一性。
 |--HashMap:底层哈希表,非同步,可以null键null值。
 |--Hashtable:底层是哈希表,同步的,不可以null键null值。
 |--TreeMap: 底层二叉树,可以对Map中的键进行排序。非同步。


1,存储:
 put();

2,判断:
 containsKey();
 containsValue();

3,取出:
 get(key);
 values();
 keySet():取出map中的键存储到set中。在通过迭代器取出键,并通过map集合的get方法获取键对应的值。
 entrySet():取出map中的键值关系,并封装成Map.Entry对象,在 将Map.Entry对象存储到Set集合中。
    通过迭代器取出Map.Entry对象,并通过Map.Entry对象的getKey,getValue获取键值。

 Map集合取出元素的方式,就是先转成Set集合。在迭代。

 

 

 

 

 

  

 

 

 

------------ <a href="http://edu.csdn.net/heima" target="blank">android培训</a>、<a href="http://edu.csdn.net/heima" target="blank">java培训</a>、期待与您交流! -----------

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值