黑马程序员05_集合collection
集合Collection
知识点一:集合概述
-
引入:
面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就要对对象进行存储,集合就是存储对象最常用的一种方式。
数组和集合类同是容器,有何不同?
数组虽然也可以存储对象,但长度是固定的;集合长度是可变的。数组中可以存储任意数据类型,集合只能存储对象。
-
集合类的特点
集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。
-
Java集合类主要由两个接口派生出来:
Collection
Set :不能存放重复对象
List :可存放重复对象,有序
Map
HashMap 、TreeMap:成对存储
Collection定义了集合框架的共性功能。
-
集合常用的方法
1、添加:
add(object):添加一个元素
addAll(Collection) :添加一个集合中的所有元素。
2、删除:
clear():将集合中的元素全删除,清空集合。
remove(obj) :删除集合中指定的对象。注意:删除成功,集合的长度会改变。
removeAll(collection) :删除部分元素。部分元素和传入Collection一致。
3、判断:
boolean contains(obj) :集合中是否包含指定元素 。
boolean containsAll(Collection) :集合中是否包含指定的多个元素。
boolean isEmpty():集合中是否有元素。
4、获取:
int size():集合中有几个元素。
5、取交集:
两个集合元素相同,返回flase;如果retainAll修改了当前集合,返回true。
6、获取集合中所有元素:
Iterator iterator():迭代器
7、将集合变成数组:
toArray();
PS:1、add方法的参数类型是Object,以便于接收任意类型对象。
2、集合中存储的都是对象的引用(地址)
迭代器
知识点一:概述
Iterator主要遍历Collection集合中的元素,也有称为迭代器或迭代精灵。
知识点二:Iterator的主要方法
boolean hasNext():若被迭代的集合元素还没有被遍历,返回true.
Object next():返回集合的下一个元素.
void remove():删除集合上一次next()方法返回的元素。(若集合中有多个相同的元素,都可以删掉)
iterator对于集合才能用,for不同,只要是循环都可用。
知识点三:取出容器中元素的方法
-
方式1:
Iterable接口(迭代遍历) 注:优先选择Iterator接口,遍历Collection里所有元素,也称为迭代器和迭代精灵;迭代是取出集合中元素的一种推荐方式。
Iterator it = l.iterator(); while (it.hasNext()) { System.out.println("迭代输出:" + it.next()); }
-
方式2:
Foreach循环 注:可以直接用,使用场合:数组和Iterable对象!
for (String str : l) { System.out.println("for集合迭代输出:" + str); }
-
方式3:
注:在()内实例化Iterable对象,进行遍历!
int i = 0;// for方法体内定义项不能出现多种不同类型 for (Iterator iter = l.iterator(); i < l.size(); i++) { System.out.println("for循环迭代实例化输出:" + iter.next());
-
方式4:
先用toArray方法输出成为数组,再用Foreach循环!
Object[] o = l.toArray(); for (Object object : o) { System.out.println("转换数组迭代输出:" + object); }
第一个和第三个很类似,第二个和第四个很类似!
Collection的子集:
|--List:元素是有序的,元素可以重复。因为该集合体系有索引。
|--ArrayList:底层的数据结构使用的是数组结构。特点:查询速度很快。但是增删稍慢。线程不同步。
|--LinkedList:底层使用的链表数据结构。特点:增删速度很快,查询稍慢。线程不同步。
|--Vector:底层是数组数据结构。线程同步。被ArrayList替代了。因为效率低。
|--Set:元素是无序,元素不可以重复。
List
List:
特有方法。凡是可以操作角标的方法都是该体系特有的方法。
知识点一:List常用的方法
增
add(index,element); / /在指定位置添加元素,如:al.add(1,"java09");
addAll(index,Collection);
删
remove(index); //删除指定位置,如:al.remove(2);
set(index,element); //修改元素,如:al.set(2,"java007");
查
get(index): //通过角标获取元素。如:get(1)
subList(from,to); 如: sub = al.subList(1,3),含第1个不含第3个
int indexOf(obj): 获取指定元素的位置。如:indexOf("java02")
知识点二:
List集合特有的迭代器:ListIterator,特有包括remove,add,set等(因为含有角标)
从集合里取出元素有两种方法,一种通过迭代器,一种通过容器。但是两者同时使用就会出现错误,如下:
List li = al.list(); while(li.hasNext()) { Object obj = li.next(); //迭代器的方法 if(obj.equals("java02")) a1.add("java009"); //容器的添加方法 }
必须使用两种相同的方法进行操作,代码如下:
List li = al.List(); while(li.hasNext()) { Object obj = li.next(); if(obj.equals("java02")) li.remove("java002"); }
可是Iterator方法是有限的,只能对元素进行判断,取出,删除的操作,如果想要其他的操作如添加,修改等,就需要使用其子接口,ListIterator。代码如下:
ListIterator li = al.listIterator(); while(li.hasNext()) { Object obj = li.next(); if(obj.equals("java02")) li.add("java009"); li.set("java006"); }
ArrayList
底层的数据结构使用的是数组结构。特点:查询速度很快。但是增删稍慢。线程不同步。
原始长度为10。
练习1
需求:去除ArrayList集合中的重复元素。
实现思想:创建一个新的容器,将要去除重复元素的容器中的每一个元素放进新的容器中,放的同时比较是否与已有元素相同,只有不相同的才放。
主要的方法的代码:
public static ArrayList singleElement(ArrayList al) { //定义一个临时容器。 ArrayList newAl = new ArrayList(); Iterator it = al.iterator(); while(it.hasNext()) { Object obj = it.next(); if(!newAl.contains(obj)) newAl.add(obj); } return newAl; } }
/*练习2
需求:将自定义对象作为元素存到ArrayList集合中,并去除重复元素。
比如:存人对象。同姓名同年龄,视为同一个人。为重复元素。
思路:
1、对人描述,将数据封装进人对象。
2、定义容器,将人存入。
3、利用上题中singleElement方法去除相同元素。
List集合判断元素是否相同,依据是元素的equals方法。
*/
-
知识点1:
al.add(new Person("lisi01",30));//al.add(Object obj);//Object obj = new Person("lisi01",30); Iterator it = al.iterator(); while(it.hasNext()) { Person p = (Person)it.next(); //传进的是Object的子类Person,需要强制转换成Person才能访问Person的方法 sop(p.getName()+"::"+p.getAge()); }
-
知识点2:利用singleElement方法去除相同元素
public static ArrayList singleElement(ArrayList al) { //定义一个临时容器。 ArrayList newAl = new ArrayList(); Iterator it = al.iterator(); while(it.hasNext()) { Object obj = it.next(); if(!newAl.contains(obj)) newAl.add(obj); } return newAl; } }
知识点3:singleElement方法中的contains方法调用的是equals,但是系统自定义的equals方法不识别相同的人。只能重写Person类中的equals方法。
代码如下:
public boolean equals(Object obj) { if(!(obj instanceof Person)) //首先判断传进去的类是否属于Person类 return false; Person p = (Person)obj; return this.name.equals(p.name) && this.age == p.age; }
LinkedList
底层使用的链表数据结构。
特点:增删速度很快,查询稍慢。线程不同步。
LinkedList:特有方法:
addFirst();
addLast();
getFirst();
getLast();
获取元素,但不删除元素。如果集合中没有元素,会出现NoSuchElementException
removeFirst();
removeLast();
获取元素,但是元素被删除。如果集合中没有元素,会出现NoSuchElementException
不用迭代器去除LinkedList中的元素:
while(!link.isEmpty())
{
sop(link.removeLast());
}
在JDK1.6出现了替代方法。
offerFirst();
offerLast();
peekFirst();
peekLast();
获取元素,但不删除元素。如果集合中没有元素,会返回null。
pollFirst();
pollLast();
获取元素,但是元素被删除。如果集合中没有元素,会返回null。
/*练习1
需求:使用LinkedList模拟一个堆栈或者队列数据结构。
堆栈:先进后出 如同一个杯子。
队列:先进先出 First in First out FIFO 如同一个水管。
思想:自己定义一个容器,利用已有容器中的方法,来实现特殊的需要。
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.removeLast(); } 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
枚举就是Vector特有的取出方式。枚举和迭代器很像。因为枚举的名称以及方法的名称都过长。所以被迭代器取代了。枚举郁郁而终了。
知识点1:遍历Vector
import java.util.*; 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()); } } }
Set
Set体系
|--Set:元素是无序(存入和取出的顺序不一定一致),元素不可以重复。
|--HashSet:底层数据结构是哈希表。是线程不安全的。不同步。
HashSet是如何保证元素唯一性的呢?
是通过元素的两个方法,hashCode和equals来完成。
如果元素的HashCode值相同,才会判断equals是否为true。
如果元素的hashcode值不同,不会调用equals。
注意,对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashcode和equals方法。
|--TreeSet:
HashSet
知识点一:hashSet中的add()方法返回的是Boolean,如果元素不重复添加成功则返回true,否则返回false
知识点二:系统中的hashCode方法自动算出传入HashSet中元素的哈希值,然后根据哈希值存储元素。也可以通过重写HashCode方法,自己设计哈希值。代码如下:
Public int hashCode()
{
return 50;
}
知识点三:hashSet保证元素唯一,是通过元素的两个方法,hashCode和equals来完成。首先比较两个元素的哈希值,如果不同,则添加,如果哈希值相同再继续调用equeals方法比较内容,内容相同则重复,否则,就添加
知识点四:如果元素的HashCode值相同,才会继续判断equals是否为true。如果元素的hashcode值不同,不会调用equals。
TreeSet
|--TreeSet:可以对Set集合中的元素进行排序。
底层数据结构是二叉树。
保证元素唯一性的依据:
compareTo方法return 0.
- TreeSet排序的第一种方式:
让元素自身具备比较性。
元素需要实现Comparable接口,覆盖compareTo方法。
这种方式也成为元素的自然顺序,或者叫做默认顺序。
- TreeSet的第二种排序方式:
当元素自身不具备比较性时,或者具备的比较性不是所需要的。
这时就需要让集合自身具备比较性。在集合初始化时,就有了比较方式。
注意:当两种排序都存在时,以比较器为主。
知识点一:可以对Set集合中的元素进行排序。按自然顺序排序
如:
TreeSet ts = new TreeSet(); ts.add(“a”); ts.add(“c”); ts.add(“b”); ts.add(“d”);
使用迭代器输出顺序为:a,b,c,d
知识点二:TreeSet元素的自定义排序
TreeSet排序的第一种方式:让元素自身具备比较性。这时需要实现Comparable接口,覆盖compareTo方法。排序时,当主要条件相同时,一定判断一下次要条件。
代码如下:
class Student implements Comparable { private String name; private int age; Student(String name,int age) { this.name = name; this.age = age; } public int compareTo(Object obj) { if(!(obj instanceof Student)) throw new RuntimeException("不是学生对象"); Student s = (Student)obj; if(this.age>s.age) //从小到大排序 return 1; if(this.age==s.age) { return this.name.compareTo(s.name); // 当主要的条件相同时,比较次要条件 } return -1; }
PS:1、TreeSet的底层数据结构为二叉树,左大右小。
2、当compareTo方法直接返回1时,用迭代器去元素时将按照存放的顺去取出。
如:
public int compareTo { return 1; }
知识点三:TreeSet的第二种排序方式。当元素自身不具备比较性时,或者具备的比较性不是所需要的。这时就需要让集合自身具备比较性。在集合初始化时,就有了比较方式。定义比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。
如:
class MyCompare implements Comparator { public int compare(Object o1,Object o2) { Student s1 = (Student)o1; Student s2 = (Student)o2; int num = s1.getName().compareTo(s2.getName()); if(num==0) { return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge())); //把年龄封装成对象用compareTo方法实现比较,返回int,写起来更简单 /* if(s1.getAge()>s2.getAge()) return 1; if(s1.getAge()==s2.getAge()) return 0; return -1; */ return num; } }
构造TreeSet对象时,代码如下:
TreeSet ts = new TreeSet(new MyCompare());
TreeSet练习1:
/*
需求:按照字符串长度排序。
思想:字符串本身具备比较性。但是它的比较方式不是所需要的。这时就只能使用比较器。
*/
TreeSet ts = new TreeSet(new StrLenComparator());//构造TreeSet class StrLenComparator implements Comparator { public int compare(Object o1,Object o2) { String s1 = (String)o1; String s2 = (String)o2; int num = new Integer(s1.length()).compareTo(new Integer(s2.length())); if(num==0) //当长度相同时,按照次要条件排序 return s1.compareTo(s2); return num; }