黑马程序员
---------------------- <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>、期待与您交流! -----------