Java基础<十一>--->集合之List、Set

时间:2022-02-16 15:15:00

第一 集合框架的概述

一、概述:

1、简述:所谓集合就是为了方便操作多个对象,对对象进行存储的一种容器。

2、集合和数组的区别:数组虽然可以存储对象,但是长度是固定的;集合长度是可变的,数组中能存储基本数据类型,集合只能存储对象。

3、集合只用于存储对象,集合长度可变,集合可以存储不同类型的对象。

4、数据结构:由于每种容器对数据的存储方式都各不相同,所以出现了不同的容器,且每种容器都有自己特有的数据结构。 二、集合体系图: Java基础<十一>--->集合之List、Set

三、List、Set集合的共性方法:

从上图可以看出List和Set的父类是Collection,所以Collection所具备的方法就是他们所共有的共性方法

Collection定义了集合框架的共性功能。

1、添加  add(e);添加一个元素        addAll(collection);添加一组collection中所有元素

2、删除  remove(e);删除一条元素       removeAll(collection);删除一组collection中所有元素clear();清空集合
3、判断。contains(e);是否包含某个元素 isEmpty();判空
4、获取 iterator();迭代器,通过迭代的方式获取集合中的每个元素,简单的说就是遍历集合。size();集合的大小,也就是获取集合中的元素个数。
5、获取交集。 retainAll();
6、集合变数组。 toArray();

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

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

ArrayList<String> arrList = new ArrayList<String>();
arrList.add("java01");
arrList.add("java02");
arrList.add("java03");
arrList.add("java04");

get_method(arrList);
c_method(arrList);
}

private static void get_method(Collection<String> c){
print(c);
/*Iterator<String> it = c.iterator();
while(it.hasNext()){
print(it.next());
}*/

//for循环的方式在内存中稍优于while,因为for循环中的局部变量随着for循环的消亡而消亡
for(Iterator<String> itr=c.iterator(); itr.hasNext(); ){
print(itr.next());
}
}

private static void c_method(Collection<String> c){
//print(c);
//c.remove("java04");//当清除的内容不存在时,返回原集合
//ArrayList<String> arl = new ArrayList<String>();
//arl.add("java01");
//arl.add("java02");
//c.removeAll(arl);
//c.clear();//清空集合。
//print(c);
print(c.contains("java01"));//是否包含
print(c.isEmpty());//判空
print(c.size());//长度

for(int x=0; x<c.toArray().length; x++){
System.out.println("collection toarray: " + c.toArray()[x]);
}
}

private static void print(Object obj){
System.out.println(obj);
}
}

四、Collection接口包含的子类: Collection接口包含最常用的子类是List与Set List:元素是有序的,元素可重复,因该集合体系有索引 Set:元素是无序的,元素不可重复 五、迭代器: 因为每种容器的数据结构都不同,所以取出的细节也就不同,所以就把迭代器定义在容器的内部,方便于访问集合内部的元素。然而他们也有共性内容就是判断和取出,就对其进行了抽取就成了Iterator接口,就可以通过Iterator的判断取出方式就可以对元素取出。在迭代时next()最好只调用一次,因为集合中的元素是奇数次的话,就会发生异常,所以具有安全隐患。 注意:1、add方法的参数类型是Object。以便于接收任意类型对象。
           2、集合中存储的都是对象的引用(地址)

第二 List集合

一、概述:

1、常见的list集合有ArrayList、LinkedList以及Vector等

2、特点:元素是有序的元素可以重复,因为该集合体系有索引。

3、List集合中常见的子类说明:

1)ArrayList:底层数据结构是数组结构。特点,查询速度很快,但增删稍慢,线程不同步。

2)LinkedList:底层使用的是链表数据结构。特点,增删速度很快,查询稍慢,线程不同步。

3)Vector:底层是数组数据结构,线程同步,被ArrayList取代,因为它的效率低。

ArrayList和Vector初始化长度都是10,当超过时,arraylist每次增加5(节约空间),而vector每次增加10

二、List集合特有的方法:

注:凡是可以操作角标的方法都是该体系特有的方法。

增:add(index,element);在指定位置添加元素   addAll(index,Collection);在指定位置上添加一组元素

删:remove(int index)移除指定位置上的元素

改:set(int index,E element)修改指定位置上的元素

查:get(index)获取指定位置上的元素。subList(from,to)获取子list。listIterator其中提供取出数据时对数据操作一些特有方法。indexOf(obj):获取指定元素的位置

1、listIterator是List特有的迭代器,是Iterator子接口。在迭代时,不可通过集合对象的方法操作集合中的元素,因为会发生ConcurrentModficationException异常。所以,在迭代时,只能用迭代器的方法操作,可Iterator方法是有限的,若想要其他操作如增删改写等,就需要使用子接口,即ListIterator,该接口只能通过List集合的listIerator方法获取。 2、在迭代时,循环中的next()调用一次,就要对hasNext判断一次,不可判断一次调用两次。 3、List集合判断元素是否相同,依据的是元素的equals方法,其中,contains和remove中就是调用的equals方法。
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)
{

//演示列表迭代器。
ArrayList al = new ArrayList();

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

sop(al);


ListIterator li = al.listIterator();


//sop("hasPrevious():"+li.hasPrevious());

while(li.hasNext())
{
Object obj = li.next();

if(obj.equals("java02"))
//li.add("java009");
li.set("java006");


}

while(li.hasPrevious())
{
sop("pre::"+li.previous());
}
//sop("hasNext():"+li.hasNext());
//sop("hasPrevious():"+li.hasPrevious());


sop(al);



/*
//在迭代过程中,准备添加或者删除元素。

Iterator it = al.iterator();

while(it.hasNext())
{
Object obj = it.next();

if(obj.equals("java02"))//用迭代器取出元素的同时,用集合的方式在操作元素,这样的并发访问有安全隐患,容易抛出异常
//al.add("java008");
it.remove();//将java02的引用从集合中删除了。对象还在内存中,只是集合中没啦。

sop("obj="+obj);


}
sop(al);
*/



}
}

三、ArrayList:
/*
将自定义对象作为元素存到ArrayList集合中,并去除重复元素。
比如:存人对象。同姓名同年龄,视为同一个人。为重复元素。

思路:
1,对人描述,将数据封装进人对象。
2,定义容器,将人存入。
3,取出。

List集合判断元素是否相同,依据是元素的equals方法。
*/
public class GetSingleObject {

public static void main(String[] args) {
ArrayList<Per> list = new ArrayList<Per>();

list.add(new Per("zhangsan01", 21));
list.add(new Per("zhangsan02", 22));
list.add(new Per("zhangsan03", 23));
list.add(new Per("zhangsan03", 23));
list.add(new Per("zhangsan04", 24));

print("remove:" + list.remove(new Per("zhangsan03", 23)));// remove底层也调用了equals进行比较

list = (ArrayList<Per>) singleElement(list);

for (ListIterator<Per> it = list.listIterator(); it.hasNext();) {
Per p = (Per) it.next();
print(p.getName() + ":::" + p.getAge());
}
}

public static List<Per> singleElement(ArrayList<Per> list) {
// 定义一个临时容器
ArrayList<Per> newList = new ArrayList<Per>();

for (ListIterator<Per> it = list.listIterator(); it.hasNext();) {
Object obj = it.next();
if (!(newList.contains(obj))) {// contains底层调用equals方法进行比较
newList.add((Per) obj);
}
}
return newList;
}

private static void print(Object obj) {
System.out.println(obj);
}
}

class Per {
private String name;// 定义姓名
private int age;// 定义年龄

Per(String name, int age) {
this.name = name;
this.age = age;
}

//重写equals方法,建立Per类自己的比较方式
public boolean equals(Object obj) {
if (!(obj instanceof Per)) {
throw new RuntimeException("类型错误");
}

Per p = (Per) obj;
return p.getName().equals(this.name) && p.getAge() == this.age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
}

四、LinkList:
1、特有方法:
addFirst();    addLast();

getFirst();    getLast();
获取元素,但不删除元素。如果集合中没有元素,会出现NoSuchElementException

removeFirst();    removeLast();
获取元素,但是元素被删除。如果集合中没有元素,会出现NoSuchElementException

在JDK1.6出现了替代方法。
offerFirst();
offerLast();

peekFirst();
peekLast();
获取元素,但不删除元素。如果集合中没有元素,会返回null。

pollFirst();
pollLast();
获取元素,但是元素被删除。如果集合中没有元素,会返回null。
import java.util.LinkedList;

/*
使用LinkedList模拟一个堆栈或者队列数据结构。

堆栈:先进后出 如同一个杯子。
队列:先进先出 First in First out FIFO 如同一个水管。
*/
public class LinkedTest {
public static void main(String[] args) {
Duilie dl = new Duilie();
dl.myAdd("java001");
dl.myAdd("java002");
dl.myAdd("java003");
dl.myAdd("java004");

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

}

class Duilie{
private LinkedList<String> link = new LinkedList<String>();

public void myAdd(String str){
link.addFirst(str);
}

public String myGet(){
return link.removeLast();//removefirst
}

public boolean isNull(){
return link.isEmpty();
}
}

五、Vector:

枚举就是Vector特有的取出方式。发现枚举和迭代器很像。其实枚举和迭代是一样的。
因为枚举的名称以及方法的名称都过长。所以被迭代器取代了。枚举郁郁而终了。

第三 Set集合

一、概述:

Set集合元素是无序(存入和取出的顺序不一定一致,在哈希表中其实是按照hash值来存放的),元素不可以重复。

二、Set集合常用子类之一HashSet:

1、概述:HashSet底层数据结构是哈希表,线程不同步。

2、HashSet保证元素唯一性:

是通过元素的两个方法,hashCode和equals来完成。如果元素的HashCode值相同,才会判断equals是否为true。如果元素的hashcode值不同,不会调用equals。(先比较哈希值,哈希值一样比较是否是同一个对象,如不是同一个对象就存放在同一位置,两个串起来,放在同一个位置上)

注意:对于判断元素是否存在,以及删除等操作,依赖的方法是元素的hashcode和equals方法。(ArrayList只依赖equals)

public class HashSetDemo {
public static void main(String[] args) {
HashSet<Course_1> hs = new HashSet<Course_1>();
hs.add(new Course_1("java01", 26));
hs.add(new Course_1("java02", 24));
hs.add(new Course_1("java03", 28));
hs.add(new Course_1("java04", 20));
hs.add(new Course_1("java04", 20));

for (Iterator<Course_1> it = hs.iterator(); it.hasNext();) {
Course_1 c = (Course_1) it.next();
System.out.println(c.getName() + "::::" + c.getPrice());
}
}
}

/*
* 课程类
*/
class Course_1 {
private String name;
private int price;

Course_1(String name, int price) {
this.name = name;
this.price = price;
}

@Override
public int hashCode() {
//System.out.println("hashcode......." + this.name);
return this.name.hashCode() + price * 20;// *20为了尽量保证哈希值不同,减少比较equals方法
}

@Override
public boolean equals(Object obj) {
if (!(obj instanceof Course_1)) {
throw new RuntimeException();
}
Course_1 c = (Course_1) obj;
System.out.println(this.name + "....equals..." + c.getName());
return c.getName().equals(this.name) && c.getPrice() == this.price;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getPrice() {
return price;
}

public void setPrice(int price) {
this.price = price;
}
}

三、Set集合常用子类之二TreeSet:

1、概述:TreeSet可以对Set集合中的元素进行排序。底层数据结构是二叉树。保证元素唯一性的依据:compareTo方法return 0.注意:当主要条件相同时还需要判断次要条件。

2、关于二叉树,见图说话:

Java基础<十一>--->集合之List、Set
二叉树的存储方式就像树叉一样,所以称为二叉树。它的特点就是把大的数放在右边,小的数放在左边,取值时会按照从小到大的顺序取值。如果数据较多,二叉树会自动折中,然后再去判断,如图中就会折中到第五个位置上。这样就大大提高了存取的效率。

3、保证元素唯一性的依据: 实现的compareTo方法的返回值,是正整数、负整数或零,则两个对象较大、较小或相同。相等时则不会存入。 4、排序的两种实现方式,让元素本身具备比较性和让容器具备比较性。

1)让元素本身具备比较性,有些元素本身就具备比较性如String和Integer,这是因为他们实现Comparable接口,这种排序方式也称为自然顺序排序。

import java.util.Iterator;
import java.util.TreeSet;

public class CV {
public static void main(String[] args) {
TreeSet<String> ts = new TreeSet<String>();
ts.add("asddf");
ts.add("kg");
ts.add("h");
ts.add("aqsdf");

for(Iterator<String> it = ts.iterator(); it.hasNext();){
System.out.println(it.next());
}
}
}

当然上面这种方式是元素本身就具备比较性,但是要是没有具备比较性那么我们就必须自己去实现comparable接口去重写compareTo方法,自己建立需要的比较性。

/*
* 往TreeSet集合中存储自定义对象学生。想按照学生的年龄进行排序。
* 因为TreeSet集合会调用到存入元素的比较性进行比较,所以如果没有比较性或者想实现自己特有的比较
* 性就需要实现comparable接口复写compareTo方法在其中实现自己特有的比较方式
*/
public class TreeSetDemo {
public static void main(String[] args) {
TreeSet<Student> ts = new TreeSet<Student>();
ts.add(new Student("lisi01", 19));
ts.add(new Student("lisi02", 15));
ts.add(new Student("lisi03", 20));
ts.add(new Student("lisi08", 30));
ts.add(new Student("lisi08", 31));
ts.add(new Student("lisi09", 31));

for (Iterator<Student> it = ts.iterator(); it.hasNext();) {
Student s = it.next();
System.out.println(s.getName() + ":::" + s.getAge());
}
}
}

class Student implements Comparable<Student> {
private String name;

private int age;

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

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
public int compareTo(Student o) {
//return 1;//这种方式是TreeSet是怎么存入的就怎么取出
//return -1;//这种方式是TreeSet是倒叙取出
System.out.println(this.name + "..compareto.." + o.getName());
if (this.age == o.getAge()) {
return this.name.compareTo(o.getName());
} else if (this.age > o.getAge()) {
return 1;
}
return -1;
}
}


2)让容器具备比较性:当元素自身不具备比较性,或者具备的比较性不是所需要的。这时需要让容器自身具备比较性。定义了比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。当两种排序都存在时,以比较器为主。实现方式:定义一个类,实现Comparator接口,覆盖compare方法。

/*
* 需求:对字符串进行按长度排序
* 分析:字符串本身具备比较性,但是是按自然顺序进行的排序,所以需要对排序方式进行重新定义,所以需要让集合具备比较性
* 使用比较器Comparator
*/
public class TestTreeSet {
public static void main(String[] args) {
TreeSet<ComString> ts = new TreeSet<ComString>(new Mycompare());
ts.add(new ComString("asd"));
ts.add(new ComString("df"));
ts.add(new ComString("dk"));
ts.add(new ComString("jkkggd"));
ts.add(new ComString("sfsfssgds"));

for (Iterator<ComString> it = ts.iterator(); it.hasNext();) {
ComString cs = it.next();
System.out.println(cs.getS());
}
}
}

class Mycompare implements Comparator<ComString> {

@Override
public int compare(ComString o1, ComString o2) {
int num = o1.getS().length() - o2.getS().length();
if (num == 0) {
return o1.getS().compareTo(o2.getS());
}
return num;
}

}

class ComString {
private String s;

ComString(String s) {
this.s = s;
}

public String getS() {
return s;
}

public void setS(String s) {
this.s = s;
}

}