在Java中,集合框架里所提到的类集实际上就是动态对象数组,因为数组本身有一个缺陷,就是长度固定。为了解决这个问题,Java里提供了动态的对象数组实现框架—Java类集框架。而在Java类集里面,提供了两个最核心的接口:Collection和Map接口。今天我来总结一下Collection接口。首先看一张图:
这是关于类集里面Collection接口的结构图,包含了子接口以及它的实现类。
Collection接口
Collection接口有两个重要的方法:
1)add()
public boolean add(E e) 向集合中添加数据
2)iterator()
public Iterator iterator() 取得Iterator接口对象,用于集合输出
1、List接口
List接口是Collection接口的子接口,除了包含Collection接口的那两个方法外,还扩充了以下两个方法:
1)get()
public E get(int index) 根据索引取得保存的数据
2)set()
public E set(int index,E element) 修改数据
List是一个接口,要想取得实例化对象,就必须有子类,在List接口下,有三个常用的子类:ArrayList、Vector、LinkedList。
1.1 ArrayList子类(优先考虑)
1.1.1 List的基本处理:
public class TestArrayList {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Hello");
list.add("Hello");
list.add("Nanfeng");
System.out.println(list);
}
}
输出结果为:
可以看出,List允许保存重复的数据
1.1.2 如果调用remove()方法,只会删除其中的一个“Hello”:
public class TestArrayList {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Hello");
list.add("Hello");
list.add("Nanfeng");
System.out.println(list.size()+"、"+list.isEmpty());
System.out.println(list);
System.out.println(list.remove("Hello"));
System.out.println(list);
}
}
1.1.3 List的get()方法
public class TestArrayList {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Hello");
list.add("Hello");
list.add("Nanfeng");
for (int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
}
}
在这里插入图片描述
1.1.5 集合操作简单Java类的时候,对于remove()、contains()方法需要覆写Objects类的equals()方法。
1.2 Vector子类(使用较少)
public class TestArrayList {
public static void main(String[] args) {
List<String> list = new Vector<>();
list.add("Hello");
list.add("Hello");
list.add("Nanfeng");
System.out.println(list);
list.remove("Hello");
System.out.println(list);
}
}
面试题:请解释ArrayList与Vector的区别:
1)ArrayList是JDK1.2提供的,而Vector是JDK1.0提供的
2)ArrayList是异步处理,性能更高,而Vector是同步处理,性能较低
3)ArrayList是线程不安全的,而Vector是性能安全的
4)ArrayList支持Iterator、ListIterator、foreach,而Vector除了支持这三个,还支持Enumeration
1.3 LinkedList子类
public class TestArrayList {
public static void main(String[] args) {
List<String> list = new LinkedList<>();
list.add("Hello");
list.add("Hello");
list.add("Nanfeng");
System.out.println(list);
list.remove("Hello");
System.out.println(list);
}
}
这个子类如果向父接口转型的话,使用形式和之前没有任何区别。
ArrayList、Vector、LinkedList的关系与区别:
- 以上三个类都是List接口下的常用子类,其中ArrayList与Vector基于数组实现,LinkedList基于双向链表实现。
- ArrayList采用懒加载策略(第一次add时才初始化内部数组,默认初始化大小为10),扩容为原先数组的1.5倍,采用异步处理,线程不安全,性能较高。
- ArrayList在大部分场合(80%,频繁查找,在集合末端插入与删除)都采用ArrayList
- Vector在产生对象时就初始化内部数组为10,扩容为原先数组的2倍。采用Synchronized修饰增删查改方法,线程安全,性能较低(锁的力度太粗,将当前集合对象锁住,读读都互斥)。Java中JDK内置的Stack是Vector子类。
- LinkedList采用异步处理,线程不安全。频繁在任意位置的插入与删除考虑使用。LinkedList是Queue接口常用子类。
2、Set接口
2.1 HashSet的使用
public class TestSet {
public static void main(String[] args) {
Set<String> set = new HashSet<>();
set.add("Hello");
set.add("Hello");
set.add("Nanfeng");
set.add("Hello");
System.out.println(set);
}
}
2.2 TreeSet的使用
public class TestSet {
public static void main(String[] args) {
Set<String> set = new TreeSet<>();
set.add("C");
set.add("C");
set.add("D");
set.add("B");
set.add("A");
System.out.println(set);
}
}
2.2.1 TreeSet排序分析
如果要进行对象数组的排序,对象所在的类一定要实现Comparable接口并且覆写compareTo()方法,只有通过此方法才能知道大小关系。
- HashSet:无序存储
- TreeSet:有序存储
- 序指的是存储该类的大小关系(内部或外部排序返回的大小关系),与插入顺序无关。
自定义类要想存储在TreeSet中,要么该类实现了内部排序(如String或Integer),要么通过构造方法传入该类的比较器(实现了外部排序Comparator接口)。
class Person2 {
private Integer age;
private String name;
public Person2(Integer age, String name) {
this.age = age;
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person2{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
//年龄升序比较器
class AgeSec implements Comparator<Person2> {
@Override
public int compare(Person2 o1, Person2 o2) {
if (o1.getAge() < o2.getAge())
return -1;
else if (o1.getAge() > o2.getAge())
return 1;
else
return 0;
}
}
//年龄降序比较器
class AgeDesc implements Comparator<Person2> {
@Override
public int compare(Person2 o1, Person2 o2) {
if (o1.getAge() < o2.getAge())
return 1;
else if (o1.getAge() > o2.getAge())
return -1;
else
return 0;
}
}
public class Test {
public static void main(String[] args) {
AgeSec ageSec = new AgeSec();
Set<Person2> set = new TreeSet<>(ageSec);
Person2 per1 = new Person2(20, "张三");
Person2 per2 = new Person2(21, "张三");
set.add(per1);
set.add(per2);
System.out.println(set);
}
}
如果这个类既传入了比较器,又天然可比较呢?这时TreeSet到底是按照什么排序呢?看例子:
class Person2 implements Comparable<Person2> {
private Integer age;
private String name;
public Person2(Integer age, String name) {
this.age = age;
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person2{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
@Override
public int compareTo(Person2 o) {
if (this.age < o.age)
return 1;
else if (this.age > o.age)
return -1;
return 0;
}
}
//年龄升序比较器
class AgeSec implements Comparator<Person2> {
@Override
public int compare(Person2 o1, Person2 o2) {
if (o1.getAge() < o2.getAge())
return -1;
else if (o1.getAge() > o2.getAge())
return 1;
else
return 0;
}
}
//年龄降序比较器
class AgeDesc implements Comparator<Person2> {
@Override
public int compare(Person2 o1, Person2 o2) {
if (o1.getAge() < o2.getAge())
return 1;
else if (o1.getAge() > o2.getAge())
return -1;
else
return 0;
}
}
public class Test {
public static void main(String[] args) {
AgeSec ageSec = new AgeSec();
Set<Person2> set = new TreeSet<>(ageSec);
Person2 per1 = new Person2(20, "张三");
Person2 per2 = new Person2(21, "张三");
set.add(per1);
set.add(per2);
System.out.println(set);
}
}
由结果来看:TreeSet优先使用外部比较器。(由compare里定义的三目运算决定)
2.2.2 重复元素判断
在使用TreeSet子类保存数据时,重复元素的判断依靠Comparable接口实现;如果使用HashSet子类保存数据,重复元素的判断要依靠Object类中的两个方法:
1)hash码:public native int hashCode();
2)对象比较:pubic boolean equals(Object obj);
代码示例:
class Person implements Comparable<Person>{
private String name;
private Integer age;
@Override
public String toString() {
return "Person{" + "name='" + name + '\'' + ",age=" + age +"}";
}
@Override
public boolean equals(Object obj) {
if (this==obj)
return true;
if (obj==null || getClass()!= obj.getClass())
return false;
Person person = (Person) obj;
return Objects.equals(name,person.name) && Objects.equals(age,person.age);
}
@Override
public int hashCode() {
return Objects.hash(name,age);
}
@Override
public int compareTo(Person o) {
if (this.age>o.age){
return 1;
}else if(this.age<o.age){
return -1;
}else {
return this.name.compareTo(o.name);
}
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
public class TestSet {
public static void main(String[] args) {
Set<Person> set = new HashSet<>();
set.add(new Person("张三",20));
set.add(new Person("张三",20));
set.add(new Person("李四",20));
set.add(new Person("王五",19));
System.out.println(set);
}
}
如果要想标识对象的唯一性,一定要equals()与hashCode()方法共同调用。
面试题:
1)如果两个对象的hashCode()相同,equals()不同结果是什么?
不能消除
2)如果两个对象的hashCode()不同,equals()相同结果是什么?
不能消除
个人建议:
保存自定义对象的时候使用List接口,保存系统类信息的时候使用Set接口(避免重复)。
3、集合输出
集合输出除了之前使用的toString()方法,List接口中的get()方法,还有四种标准方法:
Iterator、ListIterator、Enumeration、foreach
3.1 迭代输出:Iterator(重要)— 集合输出就用Iterator
Collection接口中的iterator()方法可以取得Iterator接口的实例化对象。
Iterator接口里含有三个抽象方法:
1)判断是否有下一个元素:public boolean hasNext();
2)取得当前元素:public E next();
3)删除元素:public default void remove();
3.1.1 Iterator的使用
public class TestIterator {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Hello");
list.add("Hello");
list.add("Nanfeng");
Iterator<String> iterator = list.iterator(); //实例化Iterator对象
while (iterator.hasNext()){
String str = iterator.next();
System.out.println(str);
}
}
}
集合输出的时候不要修改集合中元素
3.1.2 删除元素
public class TestIterator {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Hello");
list.add("Hello");
list.add("Nan");
list.add("Nanfeng");
list.add("Nanfeng");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
String str = iterator.next();
if(str.equals("Nan")){
iterator.remove(); //如果使用的是集合里面的remove()方法,会产生ConcurrentModificationException
continue;
}
System.out.println(str);
}
}
}
3.2 双向迭代接口:ListIterator
Iterator输出只能由前向后进行内容的迭代处理,如果要进行双向迭代,就必须依靠Iterator的子接口:ListIterator来实现。
ListIterator接口定义了两个方法:
1)判断是否有上一个元素:public boolean hasPrevious();
2)取得上一个元素:public E previous();
注意:Iterator接口对象是由Collection接口支持的,但是ListIterator是由List接口支持的
List接口提供如下方法:
3)取得ListIterator接口对象:public ListIterator listIterator();
3.2.1 ListIterator接口的使用
public class TestListIterator {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Hello");
list.add("Hello");
list.add("Nan");
list.add("Nanfeng");
ListIterator<String> listIterator = list.listIterator();
System.out.println("从前往后输出:");
while (listIterator.hasNext()){
System.out.print(listIterator.next() + "、");
}
System.out.println("\n从后往前输出:");
while (listIterator.hasPrevious()){
System.out.print(listIterator.previous()+"、");
}
}
}
注意:如果要实现从后往前输出,那么必须先从前往后输出,否则无法实现双向。
3.3 Enumeration枚举输出
Enumeration的接口定义:
1)判断是否有下一个元素:public boolean hasMoreElements();
2)取得元素:public E nextElement();
要想取得这个接口的实例化对象,只能依靠Vector子类。
3)取得Enumeration接口对象:public Enumeration elements()
3.3.1 使用Enumeration输出
public class TestEnumeration {
public static void main(String[] args) {
Vector<String> vector = new Vector<>();
vector.add("Hello");
vector.add("Hello");
vector.add("Nan");
vector.add("Nanfeng");
Enumeration<String> enumeration = vector.elements();
while (enumeration.hasMoreElements()){
System.out.println(enumeration.nextElement());
}
}
}
3.4 foreach输出
JDK1.5开始,foreach可以输出数组,其实也可以输出集合。
3.4.1 使用foreach输出
public class Testfroeach {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Hello");
list.add("Hello");
list.add("Nan");
list.add("Nanfeng");
for (String str:list){
System.out.println(str);
}
}
}
来源:CSDN
作者:king9819
链接:https://blog.csdn.net/kiing9819/article/details/104091504