欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Java语言基础之List集合以及Set集合的使用

程序员文章站 2023-12-01 16:17:40
集合类概述java.util包提供了一些集合类,这些集合又被称为容器。提到容器不难想到数组,集合类与数组的不同之处就是,数组的长度是固定的,而集合的长度是可变的,集合不能直接存储基本数据类型,另外集合也不能直接存储java对象,集合当中存储的都是对象的内存地址(或者说集合中存储的是对象的引用)。常见的有List集合、Set集合、Map集合,其中List和Set继承了Collection接口,各接口还提供了不同的实现类。集合为什么说在开发中使用较多?集合是一个容器,是一个载体,可以一次容纳多个对象。在实...

集合类概述

java.util包提供了一些集合类,这些集合又被称为容器。提到容器不难想到数组,集合类与数组的不同之处就是,数组的长度是固定的,而集合的长度是可变的,集合不能直接存储基本数据类型,另外集合也不能直接存储java对象,集合当中存储的都是对象的内存地址(或者说集合中存储的是对象的引用)。常见的有List集合、Set集合、Map集合,其中List和Set继承了Collection接口,各接口还提供了不同的实现类。

集合为什么说在开发中使用较多?
集合是一个容器,是一个载体,可以一次容纳多个对象。在实际开发中,假设连接数据库,数据库当中有10条记录,那么假设把这10条记录查询出来,在java程序中会将10条数据封装成10个java对象,然后将10个java对象放到某一个集合当中,将集合传到前端,然后遍历集合,将一个数据一个数据展现出来。

集合框架结构图

Java语言基础之List集合以及Set集合的使用

Collection接口

Collection接口是层次结构中的根接口。构成Collection的单元称为元素,Collection接口通常不能被直接使用,但该接口提供了添加元素、删除元素、管理数据的方法。由于List集合和Set集合都继承了Collection接口,因此这些方法对List集合和Set集合是通用的。

import java.util.*;

public class CollectionDemo {
   public static void main(String[] args) {
      //创建实例化集合类对象
      Collection<String> list = new ArrayList<String>();
      //添加数据
      list.add("张三");
      list.add("李四");
      list.add("王五");
      System.out.println(list);
   }
}

Collection接口的常用方法摘要

①添加:

boolean add(E e):添加一个元素
boolean addAll(Collection c):添加一堆元素

②删除:

void clear():清空容器 
boolean remove(Objec obj): 删除集合中的某个元素

③判断:

boolean contains(Object obj):判断是否包含此元素 
boolean equals(Object object):比较与指定对象是否相等 
boolean isEmpty():判断集合是否为空 

④获取:

Iterator iterator():取出 
int hashCode():返回此collection的哈希值 
int size():返回此collection中元素的个数 

代码演示

import java.util.*;

public class CollectionDemo{
	public static void main(String[] args){
		 //创建一个集合容器,使用Collection的子类ArrayList
		 ArrayList<String> al = new ArrayList<String>();      
		 //添加元素
		 al.add("zhangsan");
		 al.add("lisi");
		 al.add("wangwu");
		 al.add("zhaoliu");
		 //打印原集合
		 sop(al);
		 //删除元素
		 al.remove("lisi");
		 //打印删除元素后的集合
		 sop(al);
		 //清空集合
		 al.clear();
		 //获取集合长度
		 sop("size = "+al.size());
		 //判断元素是否存在集合中
		 sop("wangwu是否存在?"+al.contains("wangwu"));
		 //判断集合是否为空
		 sop("集合是否为空?"+al.isEmpty());
	}
	public static void sop(Object obj){
		System.out.println(obj);
	}
}

迭代器Iterator

迭代器其实就是集合取出元素的方式。
迭代器中的方法摘要:

boolean hasNext():如果集合中还有元素可以迭代,那么返回true
next():返回迭代的下一个元素
void remove():从迭代器指向的Collection中移除迭代器返回的最后一个元素

因此可以把取出方式定义在集合内部,这样取出方式就可以直接访问集合中的元素。那么取出方式就被定义成了内部类。

而每一个容器的数据结构都不同,所以取出的动作细节也不同,但是都有共性内容判断和取出。那么可以将这些共性进行抽取。

那么这些共性都符合一个规则,该规则是Iterator。如何获取集合的取出对象呢?
通过一个对外的方法iterator()。

ArrayList<String> al = new ArrayList<String>();
al.add("zhangsan");
al.add("lisi");
al.add("wangwu");
al.add("zhaoliu");
//获取迭代器,用于取出集合中的元素
Iterator it = al.iterator();
while(it.hasNext()){
   System.out.println(it.next());
}

List集合

List集合中的元素有序可重复。这里的有序是指存取的顺序有序。
List集合的特有方法
①增加:

add(int index,Object element):在指定索引处添加元素

②删除:

remove(int index):删除指定索引的元素

③查询:

get(int index):获取指定索引处的元素
subList(int from,int to):截取指定位置到指定位置的元素
listIterator():取出集合中的所有元素

④修改:

set(int index,Object element):修改指定索引处的元素

代码演示

 ArrayList<String> al = new ArrayList<String>();
 al.add("java01");
 al.add("java02");
 al.add("java03");
 al.add("java04");
 //在指定位置添加元素
 al.add(1,"java05");
 //删除指定位置的元素
 al.remove(3);
 //修改指定位置的元素
 al.set(4, "java09");
 //通过角标获取元素
 System.out.println(("get(1)="+al.get(1)));
 //获取所有元素
 for(int x=0; x<al.size(); x++) {
   System.out.println("al("+x+") = "+al.get(x));
}

List集合特有的迭代器

listInterator是Interator的子接口。在迭代时,不可以通过集合对象的方法操作集合中的元素,因为会发生异常。所以在迭代器时,只能通过迭代器的方法来操作元素,可是Interator方法有限,只能对元素判断、取出和删除的操作,如果想要其他操作如添加,修改等,就需要使用其子类ListInterator。该接口只能通过List集合的listInterator方法获取。
说白了就是ListInterator迭代器可以对集合中的元素进行增删查改。

import java.util.*;

public class ListDemo {
   public static void main(String[] args) {
      ArrayList<String> al = new ArrayList<String>();
      al.add("java01");
      al.add("java02");
      al.add("java03");
      ListIterator<String> li = al.listIterator();
      while(li.hasNext()) {
         Object obj = li.next();
         if(obj.equals("java02")) {
            //li.add("java09"); //在元素java02后面添加java09
            li.set("java06"); //将元素java02修改成java06
         }
     }
     System.out.println(al);
   }
}

注意:在迭代时,不能对集合中的元素同时进行添加、修改、删除操作,会发生异常。

ArrayList集合

当往ArrayList里面存入元素要求不重复时,比如存入学生对象,当同名同岁时,视为同一个人,则不往里面存储。
则定义学生对象时,需复写Object类中的equals方法,因为Object类中的equals方法默认是比较两个对象的地址值是否相同,而我们的需求是同名同岁才是同一个对象,所以要复写equals方法里面的比较内容。

public boolean equals(Object obj) {
   if(!(obj instanceof Student))
       return false;
   Student stu = (Student)obj;
   return this.name.equals(stu.name) && this.age == stu.age;
}

则往ArrayList集合通过add存入学生对象时,集合底层自己会调用Student类的equals方法,判断重复学生则不存入。
注:对于List集合,无论是add、contains、还是remove方法,判断元素是否相同,都是通过复写equals方法来判断!

练习:去除ArrayList集合中的重复元素。

import java.util.*;
public class ArrayListTest {
   public static void main(String[] args) {
      ArrayList<String> a = new ArrayList<String>();
        a.add("java01"); //add(Object obj);
        a.add("java02");
        a.add("java01");
        a.add("java03");
        a.add("java03"); 
        System.out.println("删除重复元素前:"+a);
        a = singleElement(a);
        System.out.println("删除重复元素后:"+a);
    }
    public static ArrayList<String> singleElement(ArrayList<String> a1) {
       //创建一个新的容器 将不重复元素存入
       ArrayList<String> newal = new ArrayList<String>();
       Iterator<String> it = a1.iterator();
       while(it.hasNext()) {
           Object obj = it.next();
           if(!newal.contains(obj))
               newal.add((String)obj);
       }
       return newa1;
   }
}

LinkedList集合

(1)Linkedist的特有方法:
①增加:

addFirst()在链表的开头插入指定的元素。
addLast()在链表末尾插入指定的元素。

②删除:

removeFirst()删除链表的第一个元素
removeLast()删除链表的最后一个元素(获取元素也删除元素)

③查询:

getFirst()获取链表的第一个元素
getLast()获取链表的最后一个元素(获取元素但不删除元素)

代码演示

 LinkedList<String> link = new LinkedList<String>();
 link.addFirst("java01");
 link.addFirst("java02");
 link.addFirst("java03");
 link.addFirst("java04");
 System.out.println(link.getFirst());
 System.out.println(link.getLast());
 //System.out.println(link.removeFirst());
 //System.out.println(link.removeLast());
 System.out.println(link.size()); //获取链表的长度
 while(!(link.isEmpty())) {
 	System.out.println(link.removeLast());
 }

练习:用LinkedList模拟堆栈和队列。堆栈:先进后出,队列:先进先出

import java.util.*;

public class LinkedListTest{
   public static void main(String[] args) {
      Stack s = new Stack(); //堆栈
      s.Myadd("java01");
      s.Myadd("java02");
      s.Myadd("java03");
      s.Myadd("java04");
      sop("Stack:"+s.Mysize());
      while(!(s.MyisEmpty()))
          sop(s.Myget());    
      Queue q = new Queue(); //队列
      q.Myadd("java01");
      q.Myadd("java02");
      q.Myadd("java03");
      q.Myadd("java04");
      sop("Queue:"+q.Mysize());
      while(!(q.MyisEmpty()))
          sop(q.Myget());
   }
   public static void sop(Object obj) {
      System.out.println(obj);
   }
}
class Stack { //模拟堆栈
   private LinkedList<String> li;
   Stack() {
      li = new LinkedList<String>();
   }
   public void Myadd(String str) {
      li.addFirst(str);
   }
   public String Myget() {
      return li.removeFirst();
   }
   public int Mysize() {
      return li.size();
   }
   public boolean MyisEmpty() {
      return li.isEmpty();
   }
}
class Queue { //模拟队列
   private LinkedList<String> link;
   Queue() {
      link = new LinkedList<String>();
   }
   public void Myadd(String str) {
      link.addFirst(str);
   }
   public String Myget() {
      return link.removeLast();
   }
   public int Mysize() {
      return link.size();
   }
   public boolean MyisEmpty() {
      return link.isEmpty();
   }
}

Vector集合

枚举是Vector特有的取出方式,枚举和迭代都是取出元素的方式,都一样。
因为枚举的名称和方法的名称都过长,所以被迭代器取代了,枚举也就郁郁而终了。

Vector v = new Vextor();
v.add("zhangsan");
v.add("lisi");
v.add("wangwu");
Enumeration en = v.elements();
while(en.hasMoreElements()) {
    System.out.println(en.nextElement());
}

总结:一般情况下,使用哪种List接口下的实现类呢?

ArrayList:底层是数组结构,每个元素都有下标。特点:查询快,线程不安全。

LinkedList:底层是链表结构,每个元素都有指向前一个元素的指针。特点:增删快

Vector:底层是数组结构,和第一个ArrayList接口一样,不过在JDK1.2之后,才有了第一个的ArrayList接口,提高了效率。

Set集合

Set集合中的元素无序不可重复(无序是指存入和取出的顺序不一致),Set集合的常用方法和Collection接口的方法一致。

Set接口常用的实现类有HashSet和TreeSet

HashSet集合

HashSet集合底层数据结构是哈希表、存取速度快、元素唯一、线程不同步。

import java.util.*;

public class HashSetDemo {
   public static void main(String[] args) {
      HashSet<String> hs = new HashSet<String>();
      System.out.println(hs.add("zhangsan"));
      System.out.println(hs.add("lisi"));
      hs.add("java02");
      hs.add("java03");
      hs.add("java04");
      Iterator<String> it = hs.iterator();
      while(it.hasNext()) {
         System.out.println(it.next());
      }
   }
}

HashSet保证性元素唯一的原理

先判断元素的hashCode值是否相同,再判断两元素的equals方法是否为true如果equals方法返回true那么就不能往HashSet集合中存(往HashSet里面存的自定义元素要复写hashCode和equals方法,以保证元素的唯一性)

HashSet的注意事项

通过new的方式往HashSet里面存的元素的hashCode都不同,但通常我们定义对象,比如学生对象时,虽然是new的两个学生对象,但是当他们name和age一样时,我们认为是同一个对象,所以为了保证元素的唯一性,我们通常在往HashSet集合里面存储元素时在定义对象的类中通常复写hashCode和equals方法。

public int hashCode() {
    return name.hashCode()+age;
}
public boolean equals(Object obj) {
    if(!(obj instanceof Student))
        return false;
    Student stu = (Student)obj;
    return this.name.equals(stu.name)&&this.age==stu.age;
}

HashSet集合是如何保证元素唯一性的呢?

如果两元素的hashCode值不同,则不会调用equals方法,
如果两元素的hashCode值相同,则继续判断equals是否返回true

hashCode和equals方法虽然定义在自定义对象类里面,但不是我们手动调用,而是往HashSet集合里面存储元素的时候,集合底层自己调用hashCode和equals,它会自己拿对象去判断,自己判断两元素是否是同一个元素。

练习:往HashSet集合中存入自定义元素人对象,姓名和年龄相同时是为同一个人,为重复元素,不能存入集合。

import java.util.*;
public class HashSetTest{  
   public static void main(String[] args) {
      HashSet<Person> hs = new HashSet<Person>();
      hs.add(new Person("zhangsan",19));
      hs.add(new Person("lisi",20));
      hs.add(new Person("wangwu",20));
      hs.add(new Person("zhangsan",19));
      Iterator<Person> it = hs.iterator();
      while(it.hasNext()) {
          Person s = (Person)it.next();
          System.out.println(s.getName()+"..."+s.getAge());
     }
  }
}
class Person {
   private String name;
   private int age;
   Person(String name,int age) {
      this.name = name;
      this.age = age;
   }
   public boolean equals(Object obj) { //复写equals方法
      if(!(obj instanceof Person))
         return false;
      Person p = (Person)obj;
      return this.name.equals(p.name) && this.age==p.age;
   }
   public int hashCode() { //复写hashCode方法
      return name.hashCode()+age;
   }
   public String getName() {
      return name;
   }
   public int getAge() {
      return age;
   }
}

TreeSet集合

TreeSet集合的底层数据结构是二叉树。可以对Set集合中的元素进行排序(自然顺序)。元素有序、线程不同步。

import java.util.*;

public class TreeSetDemo {
   public static void main(String[] args) {
      TreeSet<String> ts = new TreeSet<String>();
      ts.add("zhangsan");
      ts.add("lisi");
      ts.add("wangwu");
      Iterator<String> it = ts.iterator();
      while(it.hasNext()) {
          System.out.println(it.next());
     }
   }
}

TreeSet集合保证元素唯一性原理

①TreeSet的第一种排序方式:让元素自身具备比较性,实现Compareble接口,复写compareTo方法,此方式是元素的自然顺序

②TreeSet的第二种排序方式:当元素自身不具备比较性,或者具备的比较性不是所需要的。这时需要让容器自身具备比较性,定义一个比较器,将比较器作为参数传递给TreeSet集合的构造方法。
定义比较器:定义一个类,实现Comparator接口,复写compare方法。

当两种排序都存在时,以比较器为主。

TreeSet的注意事项

TreeSet要求往里面存的元素具备比较性,否则会报错。
TreeSet排序的第一种方式:让元素自身具备比较性。排序时,当主要条件相同时,要判断次要条件,如果次要条件也相同,那么就不能存入,因为Set是不允许有重复元素的。
TreeSet排序的第二种方式:定义对象类,实现Compareble接口,复写compareTo方法,此方式是元素的自然顺序。基本数据类型或字符串对象也都实现了Comparable接口,故同种类型基本数据间具备比较性,即自然顺序。

练习:往TreeSet集合中存储自定义对象学生,按照学生的年龄进行排序。
使用第一种排序方式:

import java.util.*;

public class TreeSetTest{
   public static void main(String[] args) {
       TreeSet<Student> ts = new TreeSet<Student>();
       ts.add(new Student("zhangsan02",22));
       ts.add(new Student("zhangsan07",20));
       ts.add(new Student("zhangsan08",19));
       ts.add(new Student("zhangsan08",19));
       Iterator<Student> it = ts.iterator();
       while(it.hasNext()) {
           Student stu = (Student)it.next();
           System.out.println(stu.getName()+"..."+stu.getAge());
      }   
   }
}
class Student implements Comparable { //强制让学生具备比较性
    private String name;
    private int age;
    Student(String name,int age){
        this.name = name;
        this.age = age;
    }
   public int compareTo(Object obj){
       if(!(obj instanceof Student))
           throw new RuntimeException("不是学生对象");
       Student s = (Student)obj;
       System.out.println(this.name+"....compareTo...."+s.name);
       if(this.age>s.age)
           return 1;
       if(this.age == age)
           return this.name.compareTo(s.name);
       return -1;
   }
   public String getName(){
       return name;
   }
   public int getAge(){
       return age;
   }
}

使用第二种排序方式:

import java.util.*;

public class TreeSetTest2 {
   public static void main(String[] args) {
       //将比较器作为参数传递给TreeSet集合的构造方法
       TreeSet<Student> ts = new TreeSet<Student>(new MyComparator());
       ts.add(new Student("zhangsan02",22));
       ts.add(new Student("zhangsan07",20));
       ts.add(new Student("zhangsan08",19));
       ts.add(new Student("zhangsan08",19));
      Iterator< Student> it = ts.iterator(); //获取迭代器
      while(it.hasNext()) {
          Student s = (Student)it.next();
          System.out.println(s.getName()+"..."+s.getAge());
      }
   }
}
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;
    }
}
class MyComparator implements Comparator<Object> { //定义比较器
   public int compare(Object o1,Object o2) { //复写compare方法
       Student s1 = (Student)o1;
       Student s2 = (Student)o2;
       int num = s1.getName().compareTo(s2.getName());
       if(num == 0) {
           if(s1.getAge() > s2.getAge())
               return 1;
           if(s1.getAge() == s2.getAge())
               return 0;
           return -1;
       }
       return num;
    }
}

TreeSet练习:按照字符串长度排序。字符串本身具备比较性,但它的比较方式不是所需要的。所以这里只能使用比较器

import java.util.*;

public class TreeSetTest {
   public static void main(String[] args) {
       TreeSet<String> ts = new TreeSet<String>(new StrLenComparator());
       ts.add("a");
       ts.add("aasfd");
       ts.add("aasd");
       ts.add("as");
       ts.add("aasdasdsa");
       Iterator<String> it = ts.iterator();
       while(it.hasNext()) {
           System.out.println(it.next());
       }
    }
}
class StrLenComparator implements Comparator<Object> { //定义比较器
    public int compare(Object o1,Object o2) {
        String s1 = (String)o1;
        String s2 = (String)o2;
        if(s1.length() > s2.length())
            return 1;
        if(s1.length() == s2.length())
            return 0;
        return -1;
    }
}

本文地址:https://blog.csdn.net/weixin_45620489/article/details/107081945