2021-08-10 集合

集合与数组的区别,缓存的管理,Collection 、List、Set 接 口的作用及相关的子类

类集设置的目的

        普通的对象数组的最大问题在于数组中的元素个数是固定的,不能动态的扩充大小,所以最 早的时候可以通过链表实现一个动态对象数组。但是这样做毕竟太复杂了,所以在 Java 中为了方便用户操作各个数据结构, 所以引入了类集的概念,有时候就可以把类集称为 java 对数据结构的实现。 在整个类集中的,这个概念是从 JDK 1.2(Java 2)之后才正式引入的,最早也提供了很多的操作类,但是并没有完 整的提出类集的完整概念。 类集中最大的几个操作接口:Collection、Map、Iterator,这三个接口为以后要使用的最重点的接口。 所有的类集操作的接口或类都在 java.util 包中。 

Collection 接口(重点)

 Collection 接口是在整个 Java 类集中保存单值的最大操作父接口,里面每次操作的时候都只能保存一个对象的数据。 此接口定义在 java.util 包中

public interface Collection<E> extends Iterable<E>

List 接口(重点)

在整个集合中 List 是 Collection 的子接口,里面的所有内容都是允许重复的

List 子接口的定义:

public interface List<E> extends Collection<E>

ArrayList(重点)

ArrayList 是 List 接口的子类,此类的定义如下:

public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, Serializable

此类继承了 AbstractList 类。AbstractList 是 List 接口的子类。AbstractList 是个抽象类,适配器设计模式

范例:增加及取得元素

package org.listdemo.arraylistdemo;
import java.util.ArrayList;
import java.util.List;
public class ArrayListDemo01 {
public static void main(String[] args) {
List<String> all = new ArrayList<String>(); // 实例化List对象,并指定泛型类型
all.add("hello "); // 增加内容,此方法从Collection接口继承而来
all.add(0, "LAMP ");// 增加内容,此方法是List接口单独定义的
all.add("world"); // 增加内容,此方法从Collection接口继承而来
System.out.println(all); // 打印all对象调用toString()方法
}
}

以上的操作向集合中增加了三个元素,其中在指定位置增加的操作是 List 接口单独定义的。随后进行输出的时候, 实际上调用的是 toString()方法完成输出的。 可以发现,此时的对象数组并没有长度的限制,长度可以任意长,只要是内存够大就行

范例:进一步操作 · 使用 remove()方法删除若干个元素并且使用循环的方式输出。 · 根据指定位置取的内容的方法,只有 List 接口才有定义,其他的任何接口都没有任何的定义

package org.listdemo.arraylistdemo;
import java.util.ArrayList;
import java.util.List;
public class ArrayListDemo02 {
public static void main(String[] args) {
List<String> all = new ArrayList<String>(); // 实例化List对象,并指定泛型类型
all.add("hello "); // 增加内容,此方法从Collection接口继承而来
all.add(0, "LAMP ");// 增加内容,此方法是List接口单独定义的
all.add("world"); // 增加内容,此方法从Collection接口继承而来
all.remove(1); // 根据索引删除内容,此方法是List接口单独定义的
all.remove("world");// 删除指定的对象
System.out.print("集合中的内容是:");
for (int x = 0; x < all.size(); x++) { // size()方法从Collection接口继承而来
System.out.print(all.get(x) + "、"); // 此方法是List接口单独定义的
            }
        }
}

Vector(重点)

与 ArrayList 一样,Vector 本身也属于 List 接口的子类,此类的定义如下:

public class Vector<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, Serializable

此类与 ArrayList 类一样,都是 AbstractList 的子类。所以,此时的操作只要是 List 接口的子类就都按照 List 进行操作。

package org.listdemo.vectordemo;
import java.util.List;
import java.util.Vector;
public class VectorDemo01 {
public static void main(String[] args) {
List<String> all = new Vector<String>(); // 实例化List对象,并指定泛型类型
all.add("hello "); // 增加内容,此方法从Collection接口继承而来
all.add(0, "LAMP ");// 增加内容,此方法是List接口单独定义的
all.add("world"); // 增加内容,此方法从Collection接口继承而来
all.remove(1); // 根据索引删除内容,此方法是List接口单独定义的
all.remove("world");// 删除指定的对象
System.out.print("集合中的内容是:");
for (int x = 0; x < all.size(); x++) { // size()方法从Collection接口继承而来
System.out.print(all.get(x) + "、"); // 此方法是List接口单独定义的
            }
        }
}

Vector 类和 ArrayList 类的区别(重点)

这两个类虽然都是 List 接口的子类,但是使用起来有如下的区别,为了方便大家笔试,列出此内容:

No.区别点ArrayListVector
1时间是新的类,是在 JDK 1.2 之后推出的是旧的类是在 JDK 1.0 的时候就定义的
2性能性能较高,是采用了异步处理性能较低,是采用了同步处理
3输出支持 Iterator、ListIterator 输除了支持 Iterator、ListIterator 输出,还支持 Enumeration 输

链表操作类:LinkedList(理解)

此类的使用几率是非常低的,但是此类的定义如下

public class LinkedList<E> extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, Serializable
import java.util.LinkedList;
import java.util.Queue;
public class TestDemo {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<String>();
queue.add("A");
queue.add("B");
queue.add("C");
int len=queue.size();//把queue的大小先取出来,否则每循环一次,移除一个元素,就少
一个元素,那么queue.size()在变小,就不能循环queue.size()次了。
for (int x = 0; x <len; x++) {
System.out.println(queue.poll());//取出元素
            }
System.out.println(queue);
        }
}

Set 接口(重点):

Set 接口也是 Collection 的子接口,与 List 接口最大的不同在于,Set 接口里面的内容是不允许重复的。 Set 接口并没有对 Collection 接口进行扩充,基本上还是与 Collection 接口保持一致。因为此接口没有 List 接口中定义 的 get(int index)方法,所以无法使用循环进行输出 那么在此接口中有两个常用的子类:HashSet、TreeSet

散列存放:HashSet(重点)

既然 Set 接口并没有扩充任何的 Collection 接口中的内容,所以使用的方法全部都是 Collection 接口定义而来的。 HashSet 属于散列的存放类集,里面的内容是无序存放的

范例:观察 HashSet

package org.listdemo.hashsetdemo;
import java.util.HashSet;
import java.util.Set;
public class HashSetDemo01 {
public static void main(String[] args) {
Set<String> all = new HashSet<String>(); // 实例化Set接口对象
all.add("A");
all.add("B");
all.add("C");
all.add("D");
all.add("E");
System.out.println(all);
        }
}

 能不能通过循环的方式将 Set 接口中的内容输出呢?

package org.listdemo.hashsetdemo;
import java.util.HashSet;
import java.util.Set;
public class HashSetDemo02 {
public static void main(String[] args) {
Set<String> all = new HashSet<String>(); // 实例化Set接口对象
all.add("A");
all.add("B");
all.add("C");
all.add("D");
all.add("E");
Object obj[] = all.toArray(); // 将集合变为对象数组
for (int x = 0; x < obj.length; x++) {
System.out.print(obj[x] + "、");
            }
        }
}

使用 HashSet 实例化的 Set 接口实例,本身属于无序的

能不能通过循环的方式将 Set 接口中的内容输出呢? 是可以实现的,因为在 Collection 接口中定义了将集合变为对象数组进行输出

package org.listdemo.hashsetdemo;
import java.util.HashSet;
import java.util.Set;
public class HashSetDemo03 {
public static void main(String[] args) {
Set<String> all = new HashSet<String>(); // 实例化Set接口对象
all.add("A");
all.add("B");
all.add("C");
all.add("D");
all.add("E");
String[] str = all.toArray(new String[] {});// 变为指定的泛型类型数组
for (int x = 0; x < str.length; x++) {
System.out.print(str[x] + "、");
        }
    }
}

排序的子类:TreeSet(重点)

与 HashSet 不同的是,TreeSet 本身属于排序的子类,此类的定义如下:

public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, Serializable

下面通过代码来观察其是如何进行排序的。

package org.listdemo.treesetdemo01;
import java.util.Set;
import java.util.TreeSet;
public class TreeSetDemo01 {
public static void main(String[] args) {
Set<String> all = new TreeSet<String>(); // 实例化Set接口对象\
all.add("D");
all.add("X");
all.add("A");
System.out.println(all);
    }
}

虽然在增加元素的时候属于无序的操作,但是增加之后却可以为用户进行排序功能的实现

排序的说明(重点)

既然 Set 接口的 TreeSet 类本身是允许排序,那么现在自定义一个类是否可以进行对象的排序呢

如果现在要是是想进行排序的话,则必须在 Person 类中实现 Comparable 接口。

package org.listdemo.treesetdemo03;
public class Person implements Comparable<Person> {
private String name;
private int age;
public int compareTo(Person per) {
if (this.age > per.age) {
return 1;
} else if (this.age < per.age) {
return -1;
} else {
return 0;
        }
}
public Person() {
}
public Person(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;
}
public String toString() {
return "姓名:" + this.name + ",年龄:" + this.age;
}
}
package org.listdemo.treesetdemo02;
import java.util.Set;
import java.util.TreeSet;
public class TreeSetPersonDemo01 {
public static void main(String[] args) {
Set<Person> all = new TreeSet<Person>();
all.add(new Person("张三", 10));
all.add(new Person("李四", 10));
all.add(new Person("王五", 11));
all.add(new Person("赵六", 12));
all.add(new Person("孙七", 13));
System.out.println(all);
        }
}

[姓名:张三,年龄:10, 姓名:王五,年龄:11, 姓名:赵六,年龄:12, 姓名:孙七,年龄:13] 从以上的结果中可以发现,李四没有了。因为李四的年龄和张三的年龄是一样的,所以会被认为是同一个对象。则 此时必须修改 Person 类,如果假设年龄相等的话,按字符串进行排序。

public int compareTo(Person per) {
if (this.age > per.age) {
return 1;
} else if (this.age < per.age) {
return -1;
} else {
return this.name.compareTo(per.name);
    }
}

关于重复元素的说明(重点)

之前使用 Comparable 完成的对于重复元素的判断,那么 Set 接口定义的时候本身就是不允许重复元素的,那么证明 如果现在真的是有重复元素的话,使用 HashSet 也同样可以进行区分

package org.listdemo.treesetdemo04;
import java.util.HashSet;
import java.util.Set;
public class HashSetPersonDemo01 {
public static void main(String[] args) {
Set<Person> all = new HashSet<Person>();
all.add(new Person("张三", 10));
all.add(new Person("李四", 10));
all.add(new Person("李四", 10));
all.add(new Person("王五", 11));
all.add(new Person("赵六", 12));
all.add(new Person("孙七", 13));
System.out.println(all);
    }
}

        此时发现,并没有去掉所谓的重复元素,也就是说之前的操作并不是真正的重复元素的判断,而是通过 Comparable 接口间接完成的。 如果要想判断两个对象是否相等,则必须使用 Object 类中的 equals()方法。 从最正规的来讲,如果要想判断两个对象是否相等,则有两种方法可以完成: · 第一种判断两个对象的编码是否一致,这个方法需要通过 hashCode()完成,即:每个对象有唯一的编码 · 还需要进一步验证对象中的每个属性是否相等,需要通过 equals()完成。 所以此时需要覆写 Object 类中的 hashCode()方法,此方法表示一个唯一的编码,一般是通过公式计算出来的

public boolean equals(Object obj) {
if (this == obj) {
return true;
    }
if (!(obj instanceof Person)) {
return false;
    }
Person per = (Person) obj;
if (per.name.equals(this.name) && per.age == this.age) {
return true;
    } else {
return false;
    }
}
public int hashCode() {
return this.name.hashCode() * this.age;
}

发现,此时已经不存在重复元素了,所以如果要想去掉重复元素需要依靠 hashCode()和 equals()方法共同完成

小结:

关于 TreeSet 的排序实现,如果是集合中对象是自定义的或者说其他系统定义的类没有实现 Comparable 接口,则不能实现 TreeSet 的排序,会报类型转换(转向 Comparable 接口)错误。 换句话说要添加到 TreeSet 集合中的对象的类型必须实现了 Comparable 接口。

不过 TreeSet 的集合因为借用了 Comparable 接口,同时可以去除重复值,而 HashSet 虽然是 Set 接口子类,但是对于没有复写 Object 的 equals 和 hashCode 方法的对象,加入了 HashSet 集合中也是不能去掉重复值的。

集合输出(重点):

已经学习过了基本的集合操作,那么对于集合的输出本身也是有多种形式的,可以使用如下的几种方式: · Iterator 迭代输出(90%)、ListIterator(5%)、Enumeration(1%)、foreach(4%) 但是在讲解输出的时候一定要记住以下的原则:“只要是碰到了集合,则输出的时候想都不想就使用 Iterator 进行输出。”

Iterator(绝对重点):

Iterator 属于迭代输出,基本的操作原理:是不断的判断是否有下一个元素,有的话,则直接输出。 此接口定义如下

public interface Iterator<E>

要想使用此接口,则必须使用 Collection 接口,在 Collection 接口中规定了一个 iterator()方法,可以用于为 Iterator 接口进行实例化操作。

此接口规定了以下的三个方法:

方法名称类型描述
1boolean hasNext普通是否有下一个元素
2E next()普通取出内容
3void remove()普通删除当前内容

通过 Collection 接口为其进行实例化之后,一定要记住,Iterator 中的操作指针是在第一条元素之上,当调用 next()方 法的时候,获取当前指针指向的值并向下移动,使用 hasNext()可以检查序列中是否还有元素

范例:观察 Iterator 输出

package org.listdemo.iteratordemo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class IteratorDemo01 {
public static void main(String[] args) {
Collection<String> all = new ArrayList<String>();
all.add("A");
all.add("B");
all.add("C");
all.add("D");
all.add("E");
Iterator<String> iter = all.iterator();
while (iter.hasNext()) {// 判断是否有下一个元素
String str = iter.next(); // 取出当前元素
System.out.print(str + "、");
        }
    }
}

以上的操作是 Iterator 接口使用最多的形式,也是一个标准的输出形式。 但是在使用 Iterator 输出的时候有一点必须注意,在进行迭代输出的时候如果要想删除当前元素,则只能使用 Iterator 接口中的 remove()方法,而不能使用集合中的 remove()方法。否则将出现未知的错误。

package org.listdemo.iteratordemo;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class IteratorDemo02 {
public static void main(String[] args) {
Collection<String> all = new ArrayList<String>();
all.add("A");
all.add("B");
all.add("C");
all.add("D");
all.add("E");
Iterator<String> iter = all.iterator();
while (iter.hasNext()) {// 判断是否有下一个元素
String str = iter.next(); // 取出当前元素
if (str.equals("C")) {
all.remove(str); // 错误的,调用了集合中的删除
            } else {
System.out.print(str + "、");
            }
        }
    }
}

此时出现了错误,因为原本的要输出的集合的内容被破坏掉了。

Iterator<String> iter = all.iterator();
while (iter.hasNext()) {// 判断是否有下一个元素
String str = iter.next(); // 取出当前元素
if (str.equals("C")) {
iter.remove(); // 错误的,调用了集合中的删除
        } else {
System.out.print(str + "、");
        }        
   }

但是,从实际的开发角度看,元素的删除操作出现的几率是很小的,基本上可以忽略,即:集合中很少有删除元素 的操作。 Iterator 接口本身可以完成输出的功能,但是此接口只能进行由前向后的单向输出。如果要想进行双向输出,则必须 使用其子接口 —— ListIterator

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值