------- android培训、java培训、期待与您交流! ----------
黑马学习日志之十 集合(一)
1 集合
集合就是一种容器,用来存放对象。
集合类的出现:面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的 操作,就对对象进行存储,集合就是存储对象最常用的一种方式。
集合的特点:只用于存储对象,集合长度可变,可以存储不同类型的对象。
集合和数组的区别:集合和数组都是容器。
数组虽然也可以存储对象,但长度是固定的,可以存储基本数据类型
集合长度是可变的,集合只能存储对象。
在实际使用中,如果长度固定,建议使用数组;如果长度不固定,建议使用集合。
2 集合框架
JDK为我们提供了一套完整的容器类库,这些类统称为集合类,它们都位于java.util包中。
整个集合类按照存储结构被分为单列集合和双列集合,单列集合的的根接口是Collection,双列集合的根接口是Map集合。在Collection接口中主要有两个子接口,分别是List和Set。Map接口的主要子接口有HashMap和TreeMap。
3 Collection
根接口: Collection
子接口: 丨—— List 可重复, 有存储顺序,有索引
丨—— Set 元素无序 不可重复
由于Collection是个接口,所以它没有构造方法,演示它的基本方法,只能通过其子类来验证。如ArrayList
方法实例:
class CollectionDemo
{
public static void main(String[] args)
{
method_get();
}
public static void method_get()
{
ArrayList al = new ArrayList();
//1,添加元素。add(Object obj);
al.add("java01");
al.add("java03");
al.add("java04");
sop(al);
}
public static void method_2()
{
ArrayList al1 = new ArrayList();
al1.add("java01");
al1.add("java02");
al1.add("java03");
al1.add("java04");
ArrayList al2 = new ArrayList();
al2.add("java03");
al2.add("java04");
al2.add("java05");
al2.add("java06");
al1.retainAll(al2); //去交集,al1中只会保留和al2中相同的元素。
al1.removeAll(al2);
sop("al1:"+al1);
sop("al2:"+al2);
}
public static void base_method()
{
//创建一个集合容器。使用Collection接口的子类。ArrayList
ArrayList al = new ArrayList();
//1,添加元素。
al.add("java01");//add(Object obj);
al.add("java02");
al.add("java03");
al.add("java04");
//打印原集合。
sop("原集合:"+al);
//3,删除元素。
al.remove("java02");
al.clear();//清空集合。
//4,判断元素。
sop("java03是否存在:"+al.contains("java03"));
sop("集合是否为空?"+al.isEmpty());
//5,获取个数。集合长度。
sop("size:"+al.size());
//打印改变后的集合。
sop(al);
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
注意:1,add方法的参数类型是Object。以便于接收任意类型对象。
2,集合中存储的都是对象的引用(地址)
4 迭代器
什么是迭代器呢?
迭代器就是集合取出元素的方式。取出方式定义在集合内部,这样取出方式可以直接访问集合内容元素,那么取出方式就被定义成了内部类。
每一个容器的数据结构不同,所以取出的动作细节也不一样,但是都有共性内容判断和取出,那么可以将共性取出。
这些内部类都符合一个规则,就是Iterator.获取集合的取出对象,通过一个对外提供的方法iterator。
迭代器是取出方式,会直接访问集合中的元素。所以将迭代器通过内部类的形式来进行描述。通过容器的iterator()方法获取该内部类的对象。
如同抓娃娃游戏机中的夹子,夹子是迭代器,取出方式被定义在容器内部,夹子的实现方式不同,但封装在内部。
例子:
class CollectionDemo
{
public static void main(String[] args)
{
method_get();
}
public static void method_get()
{
ArrayList al = new ArrayList();
//添加元素。
al.add("java01");//add(Object obj);
al.add("java02");
al.add("java03");
al.add("java04");
Iterator it = al.iterator();//获取迭代器,用于取出集合中的元素。
while(it.hasNext())
{
sop(it.next());
}
// 迭代的另一种用法
for(Iterator it = al.iterator(); it.hasNext() ; )
{
sop(it.next());
}
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
5 List集合
List集合是Collection的一个子接口,继承了Collection中的方法。
List集合:元素有序,可重复,该集合体系有索引
List子类:Vector:底层是数组数据结构,线程同步,但增删查询速度慢,被ArrayList替代。
ArrayList:底层是数组数据结构,线程不同步,查询速度快。默认容量为10
LinkedList:底层使用链表数据结构,增删速度快
List集合的特有方法:凡是可以操作角标的方法,都是该体系特有方式。
add(int i,Object obj):在指定位置添加元素
addAll(int I,Collection):在指定位置添加一堆的元素
get 使用size和get方法可以遍历集合中的元素
int indexOf(Object o):返回此列表中第一次出现的指定元素的索引,如果此列表中不包 含该元素,则返回-1
int lastIndexOf(Object o):返回此列表中最后出现的指定元素的索引:如果列表中不包 含此元素,则返回-1;
List subList(int fromIndex, int toIndex):返回列表中指定的fromIndex和toIndex之间的 部分的集合。
boolean add(E e):向list里添加元素
void add(index,E e):在index索引位插入元素
boolean remove(E e):删除元素,有这个值就返回真,没有这个值就返回假
Object remove(int index):根据索引位(角标)删除,会把删除的元素返回
E set(int index, E element):对索引位置的元素进行修改。会返回你传入的元素.
E get(int index):通过索引位(角标)获取到对应的元素
List subList(int fromIndex, int toIndex):根据指定的头尾角标获取子列表
例子:
import java.util.*;
class ListDemo
{
public static void sop(Object obj)
{
System.out.println(obj);
}
public static void method()
{
ArrayList al = new ArrayList();
//添加元素
al.add("java01");
al.add("java02");
al.add("java03");
sop("原集合是:"+al);
//在指定位置添加元素。
al.add(1,"java09");
//删除指定位置的元素。
//al.remove(2);
//修改元素。
//al.set(2,"java007");
//通过角标获取元素。
sop("get(1):"+al.get(1));
sop(al);
//获取所有元素。
for(int x=0; x<al.size(); x++)
{
System.out.println("al("+x+")="+al.get(x));
}
Iterator it = al.iterator();
while(it.hasNext())
{
sop("next:"+it.next());
}
//通过indexOf获取对象的位置。
sop("index="+al.indexOf("java02"));
List sub = al.subList(1,3);
sop("sub="+sub);
}
public static void main(String[] args)
{
method();
}
}
6 ListIterator
ListIterator是List集合特有的迭代器,ListIterator是Iterator的子接口。
在迭代时,不可以通过集合对象的方法操作集合中的元素,因为会发生并发修改异常(ConcurrentModificationException)。
所以,在迭代器时,只能用迭代器的方法操作元素,可是Iterator方法是有限的,只能对元素进行判断,取出,删除的操作如果想要其他的操作 如添加,修改等,就需要使用其子接口,ListIterator。该接口只能通过List集合的listIterator方法获取。
例子:
public static void main(String [] args){
ArrayList al = new ArrayList();
//添加元素
al.add("java01");
al.add("java02");
al.add("java03");
al.add("java04");
sop(al);
//迭代器获取所有元素
Iterator it = al.iterator();
while (it.hasNext())
{
Object obj = it.next();
if (obj.equals("java02"))
{
//al.add("java08");//并发同步异常。迭代器和集合操作不能同 时来执行
it.remove(); //iterator只能对元素进行判断取出删除操作
}
sop("obj"+obj);
}
sop(al);
}
Iterator例子:
public static void main(String[] args) {
List li = new ArrayList();
li.add("a");
li.add("A");
li.add("b");
li.add("c");
li.add("d");
ListIterator lit = list.listIterator();
while(li.hasNext())
{
Object obj =li.next();
if(obj.equals("A"))
{
li.add("e");//在java02后添加Java09
li.set("a2");//将java02改为Java006
}
}
System.out.println("----------------------------");
while(lit.hasPrevious()) { //倒序遍历
System.out.println(lit.previous());
}
7 vector
Vector是List集合的一个子类。底层是数组数据结构,线程同步,但增删查询速度慢,被ArrayList替代。
Vector中提供了一个独特的取出方式,就是枚举Enumeration。此接口Enumeration的功能与 Iterator 接口的功能是重复的Enumeration的名称和方法的名称过程,书写很麻烦。所以被Iterator所取代.
例子:
class VectorDemo{
public static void main(String[] args) {
Vector v = new Vector();
v.addElement("abc1");
v.addElement("abc2");
v.addElement("abc3");
Enumeration en = v.elements();
while(en.hasMoreElements()){
System.out.println(en.nextElement());
}
}
}
8 LinkedList
LinkedList是List的子类,底层是链表数据结构,增删快,查询慢。
LinkedList的特有方法:
添加
void addFirst(Object o): 把元素添加到第一个位置
void addLast(Object o): 把元素添加到最末的位置
获取
Object getFirst(): 获取LinkedList中的第一个元素
Object getLast(): 获取LinkedList中的最后一个元素
Peakfirst() peaklast() 也是获取元素
对于get的方法,如果集合中没有元素,就会抛出异常,
对于peak的方法,如果集合中没有元素,就会出现null
删除(通过指针被删除的元素都会返回被删除的元素)
removeLast() removeFirst(): 如果集合中没有元素,返回异常
pollLast() pollFirst():如果集合元素没有,该方法不会抛异常,null
例子:
import java.util.*;
class Duilie
{
private LinkedList list;
Duilie()
{
list = new LinkedList();
}
public void myAdd(Object obj)
{
list.addFirst(obj);
}
public Object myGet()
{
return list.removeFirst();.//先进后出
//return list.removeLast(); //先进先出
}
public boolean isNull()
{
return list.isEmpty();
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
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())
{
Duilie.sop(dl.myGet());
}
}
}
9 Set集合
set是根接口collection的一个子接口,继承了collection的方法。
set集合:元素无序(存入和取出顺序不一致),不可重复,没有索引。
Set子类:
HashSet:底层是哈希表数据结构,线程非同步。
TreeSet:底层是二叉树数据结构,可以对set集合中的元素进行排序
10 HashSet
HashSet是set集合的子类,底层是哈希表数据结构。其集合中没有重复元素。
保证元素唯一:哈希表存储结构,其实就是哈希值的存储,每个对象都有一个哈希值通过equals方法和hashCode方法来保证元素的唯一性。hash算法时计算元素的地址值的,它在对对象的存储的时候是先调用hashCode方法,然后再执行重写equals方法。
HashSet集合保证元素唯一性,依赖的是元素的hashCode方法和euqals方法。
当元素的哈希值不同时,元素都有自己的独立位置。不需要在判断元素的equals方法,
当元素的哈希值相同时,这时元素在哈希表中位置相同,这时就需要在判断一次元素的内容是否相同。就需要调用元素的equals方法进行一次比较。如果equals返回是true。那么视为两个元素为重复元素。只储存一个。如果返回是false,那么这两个元素不是重复元素,会存储在同一个哈希值上。
HashSet原理
我们使用Set集合都是需要去掉重复元素的, 如果在存储的时候逐个equals()比较, 效率较低。
哈希算法提高了去重复的效率, 降低了使用equals()方法的次数。当HashSet调用add()方法存储对象的时候, 先调用对象的hashCode()方法得到一个哈希值, 然后在集合中查找是 否有哈希值相同的对象,如果没有哈希值相同的对象就直接存入集合;如果有哈希值相同的对象, 就和哈希值相同的对象逐个进行equals()比较,比较结果为false就存入, true则不存。
注意:
类中必须重写hashCode()和equals()方法
hashCode(): 属性相同的对象返回值必须相同, 属性不同的返回值尽量不同(提高效率)
equals(): 属性相同返回true, 属性不同返回false,返回false的时候存储
例子:
class HashSetTest
{
public static void sop(Object obj)
{
System.out.println(obj);
}
public static void main(String[] args)
{
HashSet hs = new HashSet();
hs.add(new Person("a1",11));
hs.add(new Person("a2",12));
hs.add(new Person("a3",13));
hs.add(new Person("a2",12));
hs.add(new Person("a4",14));
Iterator it = hs.iterator();
while(it.hasNext())
{
Person p = (Person)it.next();
sop(p.getName()+"::"+p.getAge());
}
}
}
class Person
{
private String name;
private int age;
Person(String name,int age)
{
this.name = name;
this.age = age;
}
public int hashCode()
{
System.out.println(this.name+"....hashCode");
return name.hashCode()+age*37;
}
public boolean equals(Object obj)
{
if(!(obj instanceof Person))
return false;
Person p = (Person)obj;
System.out.println(this.name+"...equals.."+p.name);
return this.name.equals(p.name) && this.age == p.age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
}
11 TreeSet
TreeSet是set集合的子类,底层是二叉树数据结构可以对set集合中的元素进行排序,默认自然排序。
保证数据唯一性:
compareTo方法return 0为同一个元素return 1 放元素二叉树右边 return -1放元素左边
TreeSet排序方式:
TreeSet排序的第一种方式:让元素自身具备比较性。元素需要实现Comparable接口,覆盖compareTo方法,这种方式也成为元素的自然顺序 或者叫做默认排序。
例子:
import java.util.*;
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 stu = (Student)obj;
if(this.age > stu.age)
return 1;
if(this.age == stu.age) //主要特征比较不出来,用次要特征。
return this.name.compareTo(stu.name); //String类也有compareTo方法。
return -1;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
}
class TreeSetTest
{
public static void main(String[] args)
{
TreeSet ts = new TreeSet();
ts.add(new Student("sun01",22));
ts.add(new Student("sun02",21));
ts.add(new Student("sun04",21));
ts.add(new Student("sun05",20));
Iterator it = ts.iterator();
while(it.hasNext())
{
Student s = (Student)it.next();
sop(s.getName()+"======"+s.getAge());
}
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
TreeSet排序的第二种方式:当元素自身不具备比较性时,或者具备的比较性不是所需要的,这时需要让集合自身具备比较性(好比集合里面有个刻度板)。在集合的初始化时,就有了比较方式 此时要参阅集合的构造方法。
例子:
import java.util.*;
class Student
{
private String name;
private int age;
Student(String name,int age)
{
this.name = name;
this.age = age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
}
class TreeSetTest2
{
public static void main(String[] args)
{
TreeSet ts = new TreeSet(new MyComparator());
ts.add(new Student("sun001",15));
ts.add(new Student("sun002",12));
ts.add(new Student("sun006",10));
ts.add(new Student("sun002",19));
ts.add(new Student("sun004",11));
Iterator it = ts.iterator();
while(it.hasNext())
{
Student stu = (Student)it.next();
sop(stu.getName()+"====="+stu.getAge());
}
}
public static void sop(Object obj)
{
System.out.println(obj);
}
}
//TreeSet的第二种排序方式。 集合自身具备比较性。
class MyComparator 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)
{
if(s1.getAge()>s2.getAge())
return 1;
if(s1.getAge()<s2.getAge())
return -1;
return 0;
}
return num;
}
}
12 泛型
集合在初始化时没有定义数据类型,潜在安全隐患,出现了泛型。
泛型:jdk1.5版本后出现的新特性。用于解决安全问题,是一个安全机制。
泛型好处:(1)运行期间的classCastException转移到编译时期,方便程序员解决问题,让 运行期问题减少,较安全。
(2)避免强制转换麻烦。
泛型格式:通过< >来定义要操作的引用数据类型。其实< >就是来接收数据类型的。
例子:
import java.util.*;
class GenericDemo
{
public static void main(String[] args)
{
ArrayList<String> al1 = new ArrayList<String>();
al1.add("sss");
al1.add("sqq");
al1.add("saa");
sop(al1);
ArrayList<Integer> al2 = new ArrayList<Integer>();
al2.add(1);
al2.add(2);
al2.add(3);
sop(al2);
}
public static<T> void sop(ArrayList<T> al) //T代表一个明确类型
{
Iterator<T> it = al.iterator();
while(it.hasNext())
{
T t = it.next();
System.out.println(t.length());
}
}
}
泛型类
什么时候定义泛型类:当类中要操作的引用数据类型不确定的时候。早期定义Object 来完成扩展,现在定义泛型来完成扩展。
例子:
class Worker
{
}
class Student
{
}
class Utils<QQ>
{
private QQ q;
public void setObject(QQ q){
this.q = q;
}
public QQ getObject(){
return q;
}
}
class GenerinDemo3
{
public static void main(String [] args){
Utils<Worker> u= new Utils<Worker>();
u.setObject(new Worder());
Worker w = u.getObject();
}
}
泛型方法:
泛型类定义的泛型,在整个类中有效。泛型类的对象明确要操作的具体类型后,所有要操做的类型就已经固定了。
为了让不同方法可以操作不同类型,而且类型还不确定,那么可以将泛型定义在方法上。
例子:
class Demo
{
public <T> void show(T t){
System.out.println("show:"+t);
}
public <Q> void print(Q q){//该泛型只在这个方法中有效
System.out.println("print:"+t);
}
}
class GenericDemo4
{
public static void main(String [] args){
Demo d = new Demo();
d.show(new Integer(4));
d.print("hehe");
d.show("hahah");
}
}
静态泛型方法:
静态方法不可以访问类上定义的泛型
如果静态方法操作的应用数据类型不确定,可以将泛型定义在方法上
例子:
class Demo1<T>
{
public void show(T t){
System.out.println("show:"+t);
}
public <Q> void print(Q q){
System.out.println("print:"+q);
}
public static <W> void method(W t){
System.out.println("method::"+t);
}
}
public class GenericDemo5
{
public static void main(String [] args){
Demo1<String> d1 = new Demo1<String>();
d1.print(5);
d1.show("hahah");
Demo1.method("hehehehe");
}
}
泛型限定
? 通配符,也可以理解为占位符。
?extends E:可以接受E类或者E的子类型 上限
?super E:可以接受E类型或者E的父类型 下限
例子:
import java.util.*;
class Person
{
private String name;
Person(String name)
{
this.name = name;
}
public String getName()
{
return name;
}
}
class Student extends Person
{
private String name;
Student(String name)
{
super(name);
}
}
class GenericTest
{
public static void main(String[] args)
{
ArrayList<Person> al = new ArrayList<Person>();
al.add(new Person("sun01"));
al.add(new Person("sun02"));
al.add(new Person("sun03"));
sop(al);
ArrayList<Student> al1 = new ArrayList<Student>();
al1.add(new Student("sss01"));
al1.add(new Student("sss02"));
al1.add(new Student("sss03"));
sop(al1);
}
public static void sop(ArrayList<? extends Person> al)
{
Iterator<? extends Person> it = al.iterator();
while(it.hasNext())
{
System.out.println(it.next().getName());
}
}
}