黑马程序员——Java语言基础——06.集合框架(1)集合框架概述和List、Set集合

时间:2023-02-18 17:41:01

------- android培训java培训、期待与您交流! ----------

集合框架:用于存储数据的容器。

本节考点:

一、集合和数组的区别

二、说一说集合框架体系,List、Set的区别

三、HashSet集合保证元素唯一性、TreeSet两种比较方式

1-1 集合概述

特点:

1:对象封装数据,对象多了也需要存储。集合用于存储对象

2:对象的个数确定可以使用数组,但是不确定怎么办?可以用集合。因为集合是可变长度的

集合和数组的区别

1:数组是固定长度的;集合可变长度的。

2:数组可以存储基本数据类型,也可以存储引用数据类型;集合只能存储引用数据类型。

3:数组存储的元素必须是同一个数据类型;集合存储的对象可以是不同数据类型。

数据结构:就是容器中存储数据的方式。

对于集合容器,有很多种。因为每一个容器的自身特点不同,其实原理在于每个容器的内部数据结构不同。

集合容器在不断向上抽取过程中。出现了集合体系。

在使用一个体系时,原则:参阅顶层内容。建立底层对象。

黑马程序员——Java语言基础——06.集合框架(1)集合框架概述和List、Set集合


1-2 集合中常用接口和类

1-2-1 Collection接口

Collection

|--List:有序(元素存入集合的顺序和取出的顺序一致),元素都有索引。元素可以重复。

|--Set:无序(存入和取出顺序有可能不一致),不可以存储重复元素。必须保证元素唯一性。

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,取交集:

boolean  retainAll(Collection) :对当前集合中保留和指定集合中的相同的元素。如果两个集合元素相同,返回flase;如果retainAll修改了当前集合,返回true。

6,获取集合中所有元素:

Iterator  iterator()迭代器

7,将集合变成数组:

toArray();

示例:

import java.util.ArrayList;

class CollectionDemo
{
public static void main(String[] args)
{
ArrayList al1 = new ArrayList();
ArrayList al2 = new ArrayList();

al1.add("shabi01");
al1.add("shabi02");
al1.add("shabi03");
al1.add("shabi04");

al2.add("shabi01");
al2.add("shabi02");
al2.add("shabi05");
al2.add("shabi06");

//al1.addAll(al2);

//1.打印原集合
sop(al1);
sop(al2);
sop(" ");

//2.删除元素
//al1.remove("shabi01");
//al1.clear();

//3.判断元素
sop(al1.contains(al2));
sop(al1.contains("shabi01"));
sop("");

//4.获取集合长度
sop(al1.size());
sop("");

//5.取交集
al2.retainAll(al1);
sop(al1);
sop(al2);

}

public static void sop(Object o)
{
System.out.println(o);
}
}

1-2-2 Iterator接口

对 collection 进行迭代的迭代器。迭代器取代了 Java Collections Framework 中的 Enumeration。迭代器与枚举有两点不同:

(1)迭代器允许调用者利用定义良好的语义在迭代期间从迭代器所指向的 collection 移除元素。

(2)方法名称得到了改进。

方法摘要 
 boolean hasNext() :如果仍有元素可以迭代,则返回 true。 
 E next() :返回迭代的下一个元素。 
 void remove() :从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)。 
 示例:

import java.util.*;

class IteratorDemo
{
public static void main(String[] args)
{
ArrayList al1 = new ArrayList();

al1.add("shabi01");
al1.add("shabi02");
al1.add("shabi03");
al1.add("shabi04");

/*Iterator it = al1.iterator();
while (it.hasNext())
{
sop(it.next());
}*/

for (Iterator it=al1.iterator(); it.hasNext(); )
{
sop(it.next());
}

}

public static void sop(Object o)
{
System.out.println(o);
}
}

1-2-3 List接口

List本身是Collection接口的子接口,具备了Collection的所有方法。现在学习List体系特有的共性方法,查阅方法发现List的特有方法都有索引(即都带有角标),这是该集合最大的特点。

List:有序(元素存入集合的顺序和取出的顺序一致),元素都有索引。元素可以重复。

|--ArrayList:底层的数据结构是数组,线程不同步,ArrayList替代了Vector,查询元素的速度非常快。

|--LinkedList:底层的数据结构是链表,线程不同步,增删元素的速度非常快。

|--Vector:底层的数据结构就是数组,线程同步的,Vector无论查询和增删都巨慢。

1,添加:

add(index,element) :在指定的索引位插入元素。

addAll(index,collection) :在指定的索引位插入一堆元素。

2,删除:

remove(index) :删除指定索引位的元素。 返回被删的元素。

3,获取:

Object get(index) :通过索引获取指定元素。

int indexOf(obj) :获取指定元素第一次出现的索引位,如果该元素不存在返回-1;

  所以,通过-1,可以判断一个元素是否存在。

int lastIndexOf(Object o) :反向索引指定元素的位置。

List subList(start,end) :获取子列表。

4,修改:

Object set(index,element) :对指定索引位进行元素的修改。

5,获取所有元素:

ListIterator listIterator():list集合特有的迭代器。

示例:

class ArrayListDemo
{
public static void main(String[] args)
{
ArrayList al1 = new ArrayList();

al1.add("shabi01");
al1.add("shabi02");
al1.add("shabi03");
al1.add("shabi04");

sop(al1);
al1.add(2,"shabi07");//指定位置增加元素
sop("");
al1.remove(3);//移除指定位置元素
al1.set(2,"nishidasahbi");//替换指定位置元素
sop(al1);

//获取所有元素
for (int i=0; i<al1.size(); i++)
{
sop(al1.get(i));
}

sop(al1.indexOf("shabi04"));

List sub = al1.subList(1,3);//不能用ArrayList,涉及到泛型
sop(sub);

}

public static void sop(Object o)
{
System.out.println(o);
}
}

在进行list列表元素迭代的时候,如果想要在迭代过程中,想要对元素进行操作的时候,比如满足条件添加新元素。会发生.ConcurrentModificationException并发修改异常。

导致的原因是:

集合引用和迭代器引用在同时操作元素,通过集合获取到对应的迭代器后,在迭代中,进行集合引用的元素添加,迭代器并不知道,所以会出现异常情况。

如何解决呢?

既然是在迭代中对元素进行操作,找迭代器的方法最为合适.可是Iterator中只有hasNext,next,remove方法.通过查阅的它的子接口,ListIterator,发现该列表迭代器接口具备了对元素的增、删、改、查的动作。

ListIterator是List集合特有的迭代器

方法摘要:

 void add(E e) 
          将指定的元素插入列表(可选操作)。 
 boolean hasNext() 
          以正向遍历列表时,如果列表迭代器有多个元素,则返回 true(换句话说,如果 next 返回一个元素而不是抛出异常,则返回 true)。 
 boolean hasPrevious() 
          如果以逆向遍历列表,列表迭代器有多个元素,则返回 true。 
 E next() 
          返回列表中的下一个元素。 
 int nextIndex() 
          返回对 next 的后续调用所返回元素的索引。 
 E previous() 
          返回列表中的前一个元素。 
 int previousIndex() 
          返回对 previous 的后续调用所返回元素的索引。 
 void remove() 
          从列表中移除由 next 或 previous 返回的最后一个元素(可选操作)。 
 void set(E e) 
          用指定元素替换 next 或 previous 返回的最后一个元素(可选操作)。

示例:

class ListIteratorDemo
{
public static void main(String[] args)
{
ArrayList al = new ArrayList();

//添加元素
al.add("java01");
al.add("java02");
al.add("java03");

sop(al);

ListIterator li = al.listIterator();//使用ListIterator迭代器

while (li.hasNext())
{
if(li.next().equals("java02"))
//li.set("java04");
li.add("java02");
}
sop(al);
}

public static void sop(Object obj)
{
System.out.println(obj);
}
}

可变长度数组的原理:

当元素超出数组长度,会产生一个新数组,将原数组的数据复制到新数组中,再将新的元素添加到新数组中。

ArrayList:是按照原数组的50%延长。构造一个初始容量为 10 的空列表。

Vector:是按照原数组的100%延长。 

注意:对于list集合,底层判断元素是否相同,其实用的是元素自身的equals方法完成的。所以建议元素都要复写equals方法,建立元素对象自己的比较相同的条件依据

LinkedList:

特有方法:
addFirst();
addLast();
getFirst();
getLast();
获取元素,但不删除元素。如果集合中没有元素,会出现NoSuchElementException
removeFirst();
removeLast();
获取元素,但是元素被删除。如果集合中没有元素,会出现NoSuchElementException
在JDK1.6出现了替代方法。
offerFirst();
offerLast();
peekFirst();
peekLast();
获取元素,但不删除元素。如果集合中没有元素,会返回null。
pollFirst();
pollLast();
获取元素,但是元素被删除。如果集合中没有元素,会返回null

示例:

class LinkedListDemo
{
public static void main(String[] args)
{
LinkedList link = new LinkedList();

link.offerLast("java01");
link.addFirst("java02");
link.addLast("java03");
link.offerLast("java04");

while(link.offerFirst("nishi01"))//无限循环添加,真有趣
{
link.offerFirst("shabi01");
sop(link);
}


}
public static void sop(Object obj)
{
System.out.println(obj);
}
}

1-2-4 Set接口

Set接口中的方法和Collection中方法一致的。Set接口取出方式只有一种,迭代器。

|--HashSet:底层数据结构是哈希表,线程是不同步的。无序,高效;

HashSet集合保证元素唯一性:通过元素的hashCode方法,和equals方法完成的。

 如果元素的HashCode值相同,才会判断equals是否为true。
  如果元素的hashcode值不同,不会调用equals。

|--LinkedHashSet:有序,hashset的子类。

|--TreeSet:对Set集合中的元素的进行指定顺序的排序。不同步。TreeSet底层的数据结构就是二叉树

Set集合的功能和Collection是一致的。

哈希表的原理:

1,对对象元素中的关键字(对象中的特有数据),进行哈希算法的运算,并得出一个具体的算法值,这个值称为哈希值。

2,哈希值就是这个元素的位置。

3,如果哈希值出现冲突,再次判断这个关键字对应的对象是否相同。如果对象相同,就不存储,因为元素重复。如果对象不同,就存储,在原来对象的哈希值基础 +1顺延。

4,存储哈希值的结构,我们称为哈希表。

5,既然哈希表是根据哈希值存储的,为了提高效率,最好保证对象的关键字是唯一的。

这样可以尽量少的判断关键字对应的对象是否相同,提高了哈希表的操作效率。

对于ArrayList集合,判断元素是否存在,或者删元素底层依据都是equals方法。

对于HashSet集合,判断元素是否存在,或者删除元素,底层依据的是hashCode方法和equals方法。

TreeSet:

用于对Set集合进行元素的指定顺序排序,排序需要依据元素自身具备的比较性。

如果元素不具备比较性,在运行时会发生ClassCastException异常。

所以需要元素实现Comparable接口,强制让元素具备比较性,复写compareTo方法。

依据compareTo方法的返回值,确定元素在TreeSet数据结构中的位置。

TreeSet方法保证元素唯一性的方式:就是参考比较方法的结果是否为0,如果return 0,视为两个对象重复,不存。

注意:在进行比较时,如果判断元素不唯一,比如,同姓名,同年龄,才视为同一个人。

在判断时,需要分主要条件和次要条件,当主要条件相同时,再判断次要条件,按照次要条件排序。

TreeSet集合排序有两种方式,Comparable和Comparator区别:

1:让元素自身具备比较性,需要元素对象实现Comparable接口,覆盖compareTo方法。这种方式也称为元素的自然顺序,或者叫做默认顺序

2:让集合自身具备比较性,需要定义一个实现了Comparator接口的比较器,并覆盖compare方法,并将该类对象作为实际参数传递给TreeSet集合的构造函数。

第二种方式较为灵活。

需求:
往TreeSet集合中存储自定义对象学生,想按照学生的年龄进行排序

示例:第一种排序方式

class TreeSetDemo
{
public static void main(String[] args)
{
TreeSet ts = new TreeSet();

ts.add(new Student("shabi01",20));
ts.add(new Student("shabi02",24));
ts.add(new Student("shabi02",24));
ts.add(new Student("shabi05",28));
ts.add(new Student("shabi01",22));


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);
}
}

class Student implements Comparable
{
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;
}

public int compareTo(Object obj)
{
if(!(obj instanceof Student))
throw new RuntimeException("不能传入非Student对象");
Student s = (Student)obj;//强转,compareTo调用对象为Object

int num = this.name.compareTo(s.name);//使用String类中的compareTo方法比较,相同返回0,大了返回正数,小于返回负数
if (num == 0)
{
return new Integer(this.age).compareTo(new Integer(s.age));//将int型变量age,使用Integer的构造函数,封装成对象,再使用比较对象的compareTo方法
}
return num;
}
}
/*TreeSet的第二种排序方式。当元素自身不具备比较性时,或者具备的比较性不是所需要的。这时就需要让集合自身具备比较性。在集合初始化时,就有了比较方式。*/class ComparatorDemo{public static void main(String[] args){TreeSet<Student> ts = new TreeSet<Student>(new MyComparator());ts.add(new Student("shabi01",20));ts.add(new Student("shabi02",24));ts.add(new Student("shabi02",24));ts.add(new Student("shabi05",28));ts.add(new Student("shabi01",22));Iterator<Student> it = ts.iterator();while (it.hasNext()){Student s = it.next();sop(s.getName()+"...."+s.getAge());}}public static void sop(Object obj){System.out.println(obj);}}class MyComparator implements Comparator<Student>{public int compare(Student obj1,Student obj2){if(!(obj1 instanceof Student)||!(obj2 instanceof Student))throw new RuntimeException("不能传入非Student对象");Student s1 = (Student)obj1;//强转,compareTo调用对象为ObjectStudent s2 = (Student)obj2;int num = s2.getName().compareTo(s2.getName());//使用String类中的compareTo方法比较,相同返回0,大了返回正数,小于返回负数if (num == 0){return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));//将int型变量age,使用Integer的构造函数,封装成对象,再使用比较对象的compareTo方法}return num;}}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;}}

------- android培训java培训、期待与您交流! ----------