写点什么

Java 基础:集合框架之 Collection(List,Set)

  • 2022 年 7 月 10 日
  • 本文字数:9065 字

    阅读完需:约 30 分钟

前言

Collection 接口的层次结构图:

一、集合概述

所有的集合类和集合接口都在 java.util 包下。


  • 集合实际上就是一个容器。可以来容纳其它类型的数据,可以一次容纳多个对象。(数组其实就是一个集合。)

  • 集合不能直接存储基本数据类型,也不能直接存储 java 对象,集合当中存储的都是 java 对象的内存地址。(或者说集合中存储的是引用。)


  • 在 java 中每一个不同的集合,底层会对应不同的数据结构。往不同的集合中存储元素,等于将数据放到了不同的数据结构当中。(例如:数组、二叉树、链表、哈希表...以上这些都是常见的数据结构。)

  • 在 java 中集合分为两大类:


一类是单个方式存储元素:单个方式存储元素,这一类集合中超级父接口:java.util.Collection;一类是以键值对儿的方式存储元素以键值对的方式存储元素,这一类集合中超级父接口:java.util.Map;

二、Collection 详述

Collection 是 List 和 Set 的父接口

Collection 接口中的常用方法:

  • boolean add(E e) 向集合中添加元素

  • int size();获取集合中的元素个数

  • void clear();清空集合

  • boolean contains(Object o) 判断当前集合是否含有o元素

  • boolean remove(Object o) 删除集合中的元素

  • boolean isEmpty() 判断集合是否为空

  • Object[] toArray() 把集合转换为数组


示例代码(1):


import java.util.ArrayList;import java.util.Arrays;import java.util.Collection;
public class CollectionText { public static void main(String[] args) { Collection collection = new ArrayList(); //向集合中添加元素 boolean b1 = collection.add(100); boolean b2 = collection.add("你好,java"); boolean b3 = collection.add("中国"); System.out.println(b1); System.out.println(b2); System.out.println(b3); //获取集合中的元素个数 int i1 = collection.size(); System.out.println(i1);
//collection.clear(); //System.out.println(collection.size());
boolean b = collection.contains(100); System.out.println(b); //删除集合中的元素 collection.remove("中国"); System.out.println(collection.size()); //判断集合是否为空 System.out.println(collection.isEmpty()); collection.clear(); System.out.println(collection.isEmpty());
collection.add("我"); collection.add("是"); collection.add("中"); collection.add("国"); collection.add("人"); //转换为数组 Object[] objects=collection.toArray(); for(int i=0;i<objects.length;i++){ Object o=objects[i]; System.out.println(o); } System.out.println(Arrays.toString(objects)); }}
复制代码


运行结果:


truetruetrue3true2falsetrue[我, 是, 中, 国, 人]
Process finished with exit code 0
复制代码


注:Collection 在使用泛型之前,可存放 Object 的所有子类,在使用泛型之后,只能存某个具体的类型。

集合的迭代(遍历)

迭代器是一个对象


  • 所有 Collection 以及子类通用,Map 集合不能使用。

  • 使用迭代器的步骤:


第一步:获取集合对象的迭代器对象第二部:通过获取的迭代器对象进行迭代(遍历)


  • 迭代器(iterator)中的方法:


boolean hasNext(); true:有元素迭代 false:无元素迭代E next() ;返回迭代的下一个元素



示例代码(2):


import java.util.ArrayList;import java.util.Collection;import java.util.Iterator;
public class CollectionText01 { public static void main(String[] args) { //创建集合 Collection c1=new ArrayList(); c1.add(100); c1.add("abc"); c1.add("def"); c1.add(new Object()); //第一步:获取集合对象的迭代器对象iterator Iterator it=c1.iterator(); //第二步通过获取的迭代器对象进行集合遍历/迭代。 while (it.hasNext()){ Object obj=it.next(); System.out.println(obj); } }}
复制代码


运行结果:


100abcdefjava.lang.Object@723279cf
复制代码


  • 迭代器重要规律:


1.集合结果发生改变,迭代器需要重新获取 2.迭代过程中不能调用 remove()方法删除元素,会出现异常(ConcurrentModificationException)3.迭代元素的过程中使用迭代器的 remove()方法删除元素


示例代码(3):


import java.util.ArrayList;import java.util.Collection;import java.util.Iterator;
public class CollectionText05 { public static void main(String[] args) { Collection c=new ArrayList(); //ConcurrentModificationException异常!!! //Iterator it=c.iterator();
c.add(1); c.add(2); c.add(3); Iterator it=c.iterator(); while (it.hasNext()){ Object o=it.next(); // 没有更新迭代器 //c.remove();直接通过集合,没有通知迭代器(导致迭代器快照与原集合状态不同) //更新迭代器 。 it.remove(); //删除的是迭代器指向的当前对象。 System.out.println(o); } System.out.println(c.size()); }}
复制代码


运行结果:


1230
复制代码

三、List 详述

  • List 集合存储元素特点:有序,可重复!


他们都是有顺序的,也就是放进去是什么顺序,取出来还是什么顺序,也就是基于线性存储,可以看作是一个可变数组


  • List 接口下面主要有三个实现 ArrayList 、LinkedList 和 Vector

List 接口的常用方法:

  • void add(int index, E element) 在列表中指定的位置上插入指定的元素

  • E get(int index) 根据下标获取元素

  • int indexOf(Object o) 返回此列表中指定元素的第一个出现的索引

  • int lastIndexOf(Object o) 返回此列表中指定元素的最后一个发生的索引

  • E remove(int index) 移除此列表中指定下标的元素

  • E set(int index, E element) 用指定元素替换此列表中指定位置的元素


示例代码(4):


import java.util.ArrayList;import java.util.Iterator;import java.util.List;
public class ListText01 { public static void main(String[] args) { //创建list类型集合 List list=new ArrayList(); //添加元素,将指定的元素到这个列表的末尾 list.add("a"); list.add("b"); list.add("c");
//在列表中指定的位置上插入指定的元素(效率低!!!) list.add(1,"ooo");
//迭代器 Iterator it=list.iterator(); while(it.hasNext()){ Object o=it.next(); System.out.println(o); } System.out.println("=================================");
//根据下标获取元素 Object o=list.get(0); System.out.println(o);
//list集合特有的遍历方法!! for(int i=0;i<list.size();i++){ System.out.println( list.get(i)); }
// 返回此列表中指定元素的第一个出现的索引 System.out.println(list.indexOf("b")); //返回此列表中指定元素的最后一个发生的索引 System.out.println(list.lastIndexOf("c"));
System.out.println("=============================");
//移除此列表中指定位置的元素 list.remove(1); System.out.println(list.size());//3 //用指定元素替换此列表中指定位置的元素 list.set(0,"n"); System.out.println(list.get(0)); }}
复制代码


运行结果:


aooobc==============================aaooobc23=============================3n
复制代码

ArrayList 类

  • ArrayList 底层是 Object 类型的数组。

  • ArrayList 集合初始化容量 10。添加第一个元素时,创建长度为 10 的空数组。

  • 扩容机制:扩容为原容量 1.5 倍。


ArrayList 集合优化:尽可能少的扩容,数组扩容效率低。


  • ArrayList 集合优缺点:


优点:查询数据比较快,检索效率高缺点:添加和删除数据比较慢,无法存大数据量(向数组末尾添加元素效率高)


  • ArrayList 是非线程安全


示例代码(5):


import java.util.*;
public class ArrayListText01 { public static void main(String[] args) { //初始化容量为10 List list1=new ArrayList(); //初始化容量为20 List list2=new ArrayList(20);
Collection c=new ArrayList(); c.add("cccc"); c.add("bvncm"); c.add("mnkj");
Iterator it=c.iterator(); while (it.hasNext()){ Object o= it.next(); System.out.println(o); } }}
复制代码


运行结果:


ccccbvncmmnkj
复制代码

LinkedList 类

  • LinkedList 底层是基于链表数据结构。

  • LinkedList 集合优缺点:


优点:添加和删除数据比较快,随机增删效率高缺点:查询数据比较慢,检索效率低。

Vector 类

  • Vector 初始化容量是 10.

  • 扩容为原容量的 2 倍。

  • Vector 底层是数组。

  • Vector 底层是线程安全的,但是效率低

泛型机制(jdk1.5 之后新特性)

  • 只在编译时起作用,给编译器参考

  • 泛型优缺点:


优点:1.集合中存储元素类型统一 2.集合中去除掉元素是泛型指定的类型,无需进行向下转型。缺点:导致集合中元素缺乏多样性


  • JDK8 新特性:钻石表达式


List<String> list = new ArrayList<>();  类型自动推断!
复制代码


示例代码(6):

使用泛型之前

自定义类:


class Animal{    public void run(){        System.out.println("动物在移动!!");    }}class Cat extends Animal{    public void catchMouse(){        System.out.println("猫抓老鼠");    }}
class Dog extends Animal{ public void fly(){ System.out.println("狗在飞"); }}
复制代码


测试类:


import java.util.ArrayList;import java.util.Iterator;import java.util.List;
/** * jdk5.0之后的特性:泛型 */public class GenericText { //使用泛型之前 public static void main(String[] args) { List list = new ArrayList();
Cat cat = new Cat(); Dog dog = new Dog();
list.add(cat); list.add(dog);
Iterator it = list.iterator();
while (it.hasNext()) { Object o = it.next(); if (o instanceof Animal) { ((Animal) o).run(); } } }}
复制代码


运行结果:


动物在移动!!动物在移动!!
复制代码

使用泛型之后

测试类:


public class GenericText {    public static void main(String[] args) {        //使用泛型List<Animal>后,表示List集合只能存储Animal类型的元素        //泛型指定集合中存储的数据类型        List<Animal> list = new ArrayList<Animal>();
Cat cat = new Cat(); Dog dog = new Dog();
list.add(cat); list.add(dog); //表示迭代器迭代Animal类型 Iterator<Animal> it = list.iterator(); while (it.hasNext()) { //使用泛型后,迭代器每一次返回的数据是Animal类型 Animal a = it.next(); //无需强转,直接调用 a.run(); if (a instanceof Cat) { ((Cat) a).catchMouse(); } else if (a instanceof Dog) { ((Dog) a).fly(); } } }}
复制代码


运行结果:


动物在移动!!猫抓老鼠动物在移动!!狗在飞
复制代码


一起找不同吧!!!!!

四、Set 详述

  • Set 集合存储元素特点:无序不可重复。


无序表示存进去是这个顺序,取出来就不一定是这个顺序了,另外 Set 集合中元素没有下标。Set 集合中的元素还不能重复。

HashSet 类

HashSet 中的数据是无序的不可重复的。HashSet 按照哈希算法存取数据的,具有非常好性能,它的工作原理是这样的,当向 HashSet 中插入数据的时候,他会调用对象的 hashCode 得到该对象的哈希码,然后根据哈希码计算出该对象插入到集合中的位置。


  • HashSet 不是同步的;

  • 集合元素值可以是 null;


如果 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode 方法来得到该对象的 hashCode 值,然后根据该 hashCode 值决定该对象在 HashSet 中的存储位置。如果有两个元素通过 equals 方法比较 true,但它们的 hashCode 方法返回的值不相等,HashSet 将会把它们存储在不同位置,依然可以添加成功。HashSet 集合判断两个元素的标准是两个对象通过 equals 方法比较相等,并且两个对象的 hashCode 方法返回值也相等。


特别是向 HashSet 或 HashMap 中加入数据时必须同时覆盖 equals 和 hashCode 方法,应该养成一种习惯覆盖 equals 的同时最好同时覆盖 hashCode


  • Java 语法要求:


两个对象 equals 相等,那么它的 hashcode 相等两个对象 equals 不相等,那么它的 hashcode 并不要求它不相等,但一般建议不相等 hashcode 相等不代表两个对象相等(采用 equals 比较)

哈希表

  • 一个元素为链表的数组,综合了数组与链表的优点。


示例代码(7):

==没有重写 hashCode 和 equals 之前==

自定义类:


public class Student {    private String name;
public Student() {
}
public Student(String name) { this.name = name; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }}
复制代码


测试类:


import java.util.HashSet;import java.util.Set;
public class HashMapText02 { public static void main(String[] args) { Student s1 = new Student("zhangsan"); Student s2 = new Student("zhangsan");
System.out.println(s1.equals(s2)); //重写hashCode之前 System.out.println("s1hashCode值:" + s1.hashCode()); System.out.println("s2hashCode值:" + s2.hashCode());
Set<Student> set=new HashSet<>(); set.add(s1); set.add(s2); System.out.println(set.size());//2 }}
复制代码


运行结果:


falses1hashCode值:284720968s2hashCode值:1228833382
复制代码

重写 hashCode 和 equals 之后

自定义类:


public class Student {    private String name;
public Student() {
}
public Student(String name) { this.name = name; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
//equals方法 public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; return Objects.equals(name, student.name); }
//hashCode方法 public int hashCode() { return Objects.hash(name); }}
复制代码


测试类:


import java.util.HashSet;import java.util.Set;
public class HashMapText02 { public static void main(String[] args) { Student s1 = new Student("zhangsan"); Student s2 = new Student("zhangsan");
System.out.println(s1.equals(s2)); //重写hashCode之后 System.out.println("s1hashCode值:" + s1.hashCode()); System.out.println("s2hashCode值:" + s2.hashCode());
Set<Student> set=new HashSet<>(); set.add(s1); set.add(s2); System.out.println(set.size());//1 }}
复制代码


运行结果:


trues1hashCode值:-1432604525s2hashCode值:-14326045251
复制代码


找呀找呀,找不同。

TreeSet 类

  • TreeSet 是 SortedSet 接口的实现类,TreeSet 可以确保集合元素处于排序状态。

  • TreeSet 可以对 Set 集合进行排序,默认自然排序(即升序)

  • TreeSet 是可排序的

  • TreeSet 集合对自定义数据可以排序方法:


第一种:Java 提供了一个 Comparable 接口,该接口里定义了一个 compareTo(Object obj)方法,该方法返回一个整数值,实现该接口的类必须实现该方法,实现了该接口的类必须实现该方法,实现接口的类就可以比较大小了。当调用一个一个对象调用该方法与另一个对象进行比较 obj1.compareTo(obj2)如果返回 0 表示两个对象相等;如果返回正整数则表明 obj1 大于 obj2,如果是负整数则相反。第二种:在构造 TreeSet 或 TreeMap 集合时给他一个比较器对象。比较规则自己写!!!


  • Comparable 与 Comparator 区别?


当比较规则不会发生改变时或比较规则只用一个时建议实现 Comparable 接口当比较规则有多个,并且需要多个比较规则之间进行切换,建议使用与 Comparator 编写比较器可以改变规则!!!

Comparator 区别符合 OCT 原则

红黑树(自平衡二叉树)

  • TreeSet 内部实现的是红黑树,默认整形排序为从小到大。

  • 三种遍历方式:前序遍历,中序遍历,后序遍历。(前中后指的是根的位置)

  • TreeSet 采用中序遍历方式。




示例代码(8):


import java.util.TreeSet;
public class TreeMapText01 { public static void main(String[] args) { // TreeSet<String> ts=new TreeSet(); ts.add("zhangsan"); ts.add("wangwu"); ts.add("make"); ts.add("langlang"); for (String s: ts){ System.out.println(s); } System.out.println("============================="); TreeSet<Integer> ts2=new TreeSet(); ts2.add(200); ts2.add(300); ts2.add(600); ts2.add(14); for (Integer i:ts2){ System.out.println(i); } }}
复制代码


运行结果:


langlangmakewangwuzhangsan=============================14200300600
Process finished with exit code 0
复制代码


从以上结果可以看出,String 类和 Integer 类都实现了这个接口。示例代码(9):

实现 Comparable 接口

Customer 类:


class Customer implements Comparable<Customer>{    int age;
public Customer(int age) { this.age = age; } //需要在此方法写比较的逻辑,或者说出比较规则,按照什么进行比较!! //比较规则自己定
public int compareTo(Customer o) {//c1.comperTo(c2); //this是c1,o是c2 //c1与c2比较,就是this与c比较 // return this.age-o.age; return o.age-this.age; }
@Override public String toString() { return "Customer{" + "age=" + age + '}'; }}
复制代码


测试类:


import java.util.TreeSet;
public class TreeSetText03 { public static void main(String[] args) { Customer p = new Customer(23); Customer p2 = new Customer(100); Customer p3 = new Customer(30); Customer p4 = new Customer(65); Customer p5 = new Customer(46);
TreeSet<Customer> treeSet = new TreeSet<>();
treeSet.add(p); treeSet.add(p2); treeSet.add(p3); treeSet.add(p4); treeSet.add(p5); for (Customer c : treeSet) { System.out.println(c); } }}
复制代码


运行结果:


Customer{age=100}Customer{age=65}Customer{age=46}Customer{age=30}Customer{age=23}
复制代码


示例代码(10):

比较器进行排序

WuGui 类:


class WuGui {    int age;
public WuGui(int age) { this.age = age; }
@Override public String toString() { return "WuGui{" + "age=" + age + '}'; }}
复制代码


比较器:


//单独在这里编写一个比较器//比较实现java.util.Comparator接口。(Comparable是java.lang包下的,Comparator接口是java.util包下)
class WuguiComparator implements Comparator<WuGui> {
public int compare(WuGui o1, WuGui o2) { return o1.age - o2.age; }}
复制代码


测试类:


import java.util.Comparator;import java.util.TreeSet;
public class TreeSetText05 { public static void main(String[] args) { //创建TreeSet时,需要使用比较器 //TreeSet<WuGui> wuGuis=new TreeSet<>();这样不行,没有通过构造方法构造一个比较器进去 //给构造方法添加一个比较器 TreeSet<WuGui> wuGuis = new TreeSet<>(new WuguiComparator()); wuGuis.add(new WuGui(200)); wuGuis.add(new WuGui(30)); wuGuis.add(new WuGui(50)); wuGuis.add(new WuGui(10)); wuGuis.add(new WuGui(100));
for (WuGui w : wuGuis) { System.out.println(w); } }}
复制代码


运行结果:


WuGui{age=10}WuGui{age=30}WuGui{age=50}WuGui{age=100}WuGui{age=200}
复制代码


改进上面测试类(使用匿名内部类)代码如下:


public class TreeSetText05 {    public static void main(String[] args) {  //第三种!! //使用匿名内部类的方式  这个类没有名字,直接new接口!!        TreeSet<WuGui> wuGuis = new TreeSet<>(new Comparator<WuGui>() {            @Override            public int compare(WuGui o1, WuGui o2) {                return o1.age - o2.age;            }        });        wuGuis.add(new WuGui(200));        wuGuis.add(new WuGui(30));        wuGuis.add(new WuGui(50));        wuGuis.add(new WuGui(10));        wuGuis.add(new WuGui(100));
for (WuGui w : wuGuis) { System.out.println(w); } }}
复制代码


发布于: 2022 年 07 月 10 日阅读数: 3
用户头像

该来的总会来,或迟或早。🎈 2022.06.13 加入

有JAVA方面3年学习经验,csdn/51cto等平台优质作者,华为云云享专家、阿里云受邀专家博主,擅长JAVA,大数据等方面的技术等。

评论

发布
暂无评论
Java基础:集合框架之Collection(List,Set)_集合_百思不得小赵_InfoQ写作社区