Java_集合(Set、List、Queue)

什么是集合:

Java中的集合,就是保存对象的集合。是一种只能用来保存对象的集合

Java集合包括四种:Set、List、Queue、Map。

其中Set代表无序、不可重复的集合。List代表有序、重复的集合。Map代表具有映射关系的集合。Queue代表一种队列集合实现。

Java中的集合类主要有两个接口派生而来:Collection接口和Map接口。


Collection接口是List接口、Queue接口、List接口的父接口。

该接口里定义的方法可以用于操作Set集合、Queue集合和List集合。Collection接口中有如下操作元素的方法:

boolean add(Object o):向集合中添加一个元素,添加成功返回true。

boolean addAll(Collection c):将集合c中的所有元素添加到指定集合中,添加成功返回true。

void clear():清除集合里的所有元素,将集合长度变为0.

boolean contains(Object o):返回集合里是否包含指定元素。

boolean containsAll(Collection c):返回集合里是否包含集合c中的全部元素。

boolean isEmpty():返回集合是否为空。

Iterator iterator():返回Iterator对象,用于遍历集合里的元素。

boolean remove(Object o):删除集合中的指定元素o。当集合中包含一个或多个元素o时,该方法只删除第一个符合条件的元素,返回true。

boolean removeAll(Collection c):从集合中删除集合c所包含的元素,如果删除一个或一个以上元素,返回true。

boolean retainAll(Collection c):从集合删除集合c中不包含的元素。

int size():返回集合中元素的个数。

Object[] toArray():该方法把集合转化为一个数组,所有集合元素变为相应的数组元素。

package Suanfa;
import java.util.*;

public class Collection1 {
	public static void main(String[] args) {
		Collection c=new ArrayList();
		c.add(12);
		c.add(9);
		c.add(23);
		c.remove(23);
		System.out.println(c);//[12,9]
		System.out.println(c.contains(12));//true
		Collection d=new HashSet();
		d.add(109);
		d.add(102);
		d.add(12);
		System.out.println(c.containsAll(d));//false
		System.out.println(c.removeAll(d));//true
		System.out.println(c);//[9]
		c.add(11);
		c.add(109);
		System.out.println(d.retainAll(c));//true从集合中删除集合c不包含的元素
		System.out.println(d);//[109]相当于集合c和d的交集。
		//d.clear();
		c.toArray();//将集合c变成数组
		System.out.println(c);
	}
}


创建了两个不同的类型的Collection集合,c是ArrayList集合,d是HashSet集合。虽然实现类不同,但把他们当成Collection来使用可以实现Collection接口的所有方法。

Set集合

Set集合与Collection基本相同,没有提供额外的方法,但是Set集合不允许添加重复的元素,当使用add()方法添加相同的元素会返回false。

Set集合有三个典型的实现类:HashSet、TreeSet、EnumSet。

HashSet类

1不能保证元素的排列顺序,与添加顺序可能相同也可能不同。

2HashSet不是同步的,多个线程修改HashSet,必须通过代码保证同步。

3.HashSet可以为空。

HashSet判断两个元素相等的标准是两个对象通过equals()方法比较相等,并且两个对象的HashCode()方法返回值也相等。

package Suanfa;
import java.util.*;

public class Collection2 {
	public static void main(String[] args) {
		HashSet c=new HashSet();
		c.add(new A());
		c.add(new A());
		c.add(new B());
		c.add(new B());
		c.add(new C());
		c.add(new C());
		System.out.println(c);
		//结果为:[Suanfa.A@7852e922, Suanfa.B@1, Suanfa.B@1, Suanfa.C@2, Suanfa.A@4e25154f]
	}
}
class A{
	public boolean equals(Object obj) {
		return true;
	}
}
class B{
	public int hashCode() {
		return 1;
	}
}
class C{
	public int hashCode() {
		return 2;
	}
	public boolean equals(Object obj) {
		return true;
	}
}

即使两个A对象的equals返回都为true,两个B对象的hashCode()返回都是1,HashSet仍然把他们当做两个对象。

只有当equals返回相等,hashCode()返回值也相等,HashSet才把他们当成两个对象。

所以,如果需要重写equals方法,则也应该重写hashCode()方法,否则如果equals()返回相等,但是hashCode()返回不等,HashSet会把同一个元素存在Hash表的不同位置。使得两个对象都添加成功,这样不符合HashSet的规则。

如果hashCode()方法返回的hashCode值相同,但是equals返回false。会导致在该位置用链式存储结构来保存多个对象。HashSet访问集合元素是根据HashCode值来快速定位的,一个hashCode位置有两个元素,会导致访问性能的下降。

LInkedHashSet类

LInkedHashSet类是HashSet的子类

LInkedHashSet具有HashSet的特性,也是根据元素的hashCode值来决定元素的存储位置,但是是使用链表来维护元素的次序。元素的顺序与添加的顺序相同。


TreeSet类

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

与HashSet集合相比,TreeSet还有如下方法:

Comparator comparator():如果TreeSet采用定制排序,则该方法返回定制排序所使用的Compator,如果采用自然排序,则返回null。

Object first():返回第一个元素。

Object last():返回最后一个元素。

Object lower(Object e):返回集合元素e之前的元素。

Object high(Object e):返回集合元素e之后的元素。

SortedSet subSet(from.to):返回从from到to之间的元素。

SortedSet headSet(Object to):返回小于to的元素子集。

SortedSet tailSet(Object from):返回大于from的元素子集。


EnumSet类

EnumSet是专门为枚举类设计的集合类,EnumSet中的所有元素必须是指定枚举类型的枚举值,在创建EnumSet显式或隐式地指定。EnumSet是有序的,是按照枚举类中的顺序。

EnumSet适用于批量操作,不能添加null元素。

EnumSet没有构造器创建该类的实例,程序应该通过它的类方法来创建EnumSet对象。

EnumSet allOf(Calss elementType):创建一个包含指定枚举类里所有枚举值的EnumSet集合。

EnumSet complementOf(EnumSet s):创建了一个与指定集合s相同的EnumSet集合,包含了枚举类中除s之外的元素。

EnumSet copyOf(Collection c):使用一个普通的集合来创建EnumSet类。

EnumSet copyOf(EnumSet s):创建一个与指定元素EnumSet具有相同元素类型、相同集合元素的EnumSet集合。

EnumSet noneOf(Class elementType):创建一个元素类为指定枚举类型的空EnumSet。

EnumSet of(E first,E...rest):创建一个或多个枚举值的EnumSet集合。

EnumSet range (E from,E to):创建一个从from到to的枚举范围内所有枚举值得EnumSet集合。

package Suanfa;
import java.util.*;

public class Collection3 {
	public static void main(String[] args) {
		EnumSet es1=EnumSet.allOf(Season.class);
		System.out.println(es1);//[Spring, Summer, Fail, Winter]
		EnumSet es2=EnumSet.copyOf(es1);
		System.out.println(es2);//[Spring, Summer, Fail, Winter]
		EnumSet es3=EnumSet.noneOf(Season.class);
		es3.add(Season.Spring);
		es3.add(Season.Fail);
		System.out.println(es3);//[Spring, Fail]
		EnumSet es4=EnumSet.of(Season.Summer,Season.Winter);
		System.out.println(es4);//[Summer, Winter]
		EnumSet es5=EnumSet.range(Season.Spring, Season.Fail);
		System.out.println(es5);//[Spring, Summer, Fail]
		EnumSet es6=EnumSet.complementOf(es5);
		System.out.println(es6);//[Winter]
	}
}

enum Season{
	Spring,Summer,Fail,Winter
}

各Set实现类的性能分析

HashSet的性能比TreeSet性能好,因为TreeSet需要额外的红黑树算法来维护集合元素的次序。

HashSet还有一个子类:LinkedHashSet,对于普通的插入、删除操作,LinkedHashSet比HashSet要慢一点,这是因为维护链表所带来额外开销造成的,但由于有LinkedHashSet,遍历会更快。

EnumSet是Set实现类中性能最好的,但只能保存同一个枚举类中的枚举值。

Set的三个实现类都是线程不安全的,如果有多个线程同时访问一个Set集合,并且有超过一个线程修改了Set集合,则必须手动保证该Set集合的同步性。通过Collections工具类的synchronizedSortedSet方法来包装该Set集合。并且该操作最好在创建时进行,以防止对Set集合的意外非同步访问。


List集合

List集合代表有序的、可重复的集合,集合中的每个元素都有其对应的顺序索引。List集合允许使用重复元素,可以通过索引来指定位置的集合元素,List集合默认按元素的添加顺序设置元素的索引号。

List接口的方法:

void add(int index,Object element):将元素element插入到索引为index处。

boolean addAll(int index,Collection c):将集合c中的所有元素添加到index处。

Object get(int index):返回集合index处的元素。

int indexOf(Object o):返回对象o在集合第一次出现的索引位置。

int lastIndexOf(Object o):返回对象o在集合最后一次出现的索引位置。

Object remove(int index):删除Index处元素,并返回。

Object set(int index,Object element):将Index处的元素替换成element,并返回被替换的旧元素。

List subList(int fromIndex,int toIndex):返回从索引fromIndex(包含)到toIndex(不包含)的所有元素组成的子集合。

void replaceAll(unaryOperator operator):根据operator指定的计算规则重新设置List集合的所有元素。

void sort(Comparator c):根据Comparator参数对List集合元素进行排序。

package Suanfa;
import java.util.*;
public class Collection4 {
	public static void main(String[] args) {
		List books=new ArrayList();
		books.add(new String("hello"));
		books.add(new String("Thank you"));
		books.add(new String("Thank you very much!"));
		System.out.println(books);//[hello, Thank you, Thank you very much!]
		books.add(1,new String("how are you?"));
		for(int i=0;i<2;i++) {
			System.out.println(books.get(i));//hello
											//how are you?
		}
		books.add(new String("Thank you"));
		System.out.println(books);//[hello, how are you?, Thank you, Thank you very much!, Thank you]
		System.out.println(books.indexOf(new String("Thank you")));//2
		System.out.println(books.lastIndexOf(new String("Thank you")));//4
		books.set(0, new String("hi"));
		List book2=books.subList(0, 2);
		System.out.println(book2);//[hi, how are you?]
		books.remove(new String("Thank you very much!"));
		System.out.println(books);//[hi, how are you?, Thank you, Thank you]
		books.sort((o1,o2)->((String)o1).length()-((String)o2).length());
		System.out.println(books);//上程序采用Lambda表达式实现了comparator接口,控制元素从小到大排序。 
		//[hi, Thank you, Thank you, how are you?]
		//books.replaceAll(ele->((String)ele).length());//实现UnaryOperator接口
		//System.out.println(books);//[2,9,9,12]
		ListIterator lit=books.listIterator();
		while(lit.hasNext()) {
			System.out.println(lit.next());
			lit.add("--------");
		}
		System.out.println("--分隔符--");
		while(lit.hasPrevious()) {
			System.out.println(lit.previous());
		}	
	}
}

实际List集合中并未包含该字符串对象,List集合中添加的字符串对象时,添加的是通过new关键字创建的新字符串对象。indexOf(new String("hello"))也是创建的新对象,和原有对象不是一个对象,通过equals比较是相同内容。

Set只提供一个iterator()方法,List还额外提供一个listIterator方法,该方法返回一个ListIterator对象,ListIterator接口继承了Iterator接口,并提供了专门操作List的方法。

boolean hasPrevious():返回该迭代器关联的集合中是否还有上一个元素。

Object previous():返回该迭代器的上一个元素。

void add(Object e):在指定位置插入一个元素。


ArrayList和Vector实现类

ArrayList和Vector是List接口的两个典型实现类。ArrayList和Vector封装了一个动态允许再分配的Object[]数组。ArrayList和Vector使用initialCapacity参数来设置数组的长度,当元素超过时,会自动增加。当向ArrayList或Vector添加大量元素时,可以使用enusureCapacity(int minCapacity)方法一次性增加initailCapacity。当创建空的ArrayList或Vector时,默认长度为10。还提供一个方法trimToSize(),调整ArrayList或Vector使数组长度为元素个数。

ArrayList和Vector显著区别是:ArrayList是线程不安全的,Vector是线程安全的。

即使需要保证List集合线程安全,仍然不推荐使用Vector。后面会提供一个Collections类可以将ArrayList变成线程安全的。

Vector还提供一个Stack子类,用于模拟栈这种数据结构。提供如下方法:

Object peek():返回栈顶元素,不出栈。

Object pop():返回栈顶元素,并出栈。

void push(Object item):将一个元素压入栈。


Arrays.ArrayList与ArrayList

Arrays类中提供了asList(Object...a)方法,该方法可以把一个数组或指定个数的对象变成一个LIst集合,这个List集合既不是ArrayList实现类的实例也不是Vector实现例的实例,而是Arrays的内部类ArrayList的实例。

Arrays.ArrayList是一个固定长度的List集合,程序只能遍历访问该集合中的元素,不可增删该集合中的元素。

package Suanfa;

import java.util.Arrays;
import java.util.List;
public class Collection5 {
	public static void main(String[] args) {
		List fixedList=Arrays.asList("hello","Thank you","Thank you very much");
		System.out.println(fixedList.getClass());
		fixedList.forEach(System.out::println);
		//fixedList.add("g");//出现异常
	}
}


Queue集合

Queue是用于模拟队列数据结构,队列是先进先出。有如下方法:

void add(Object e):将指定元素添加至队尾。

Object element():获取队列的头部元素,但不删除该元素。

boolean offer(Object e):将指定元素添加至队尾。当使用有容量限制的队列时,此方法比add(Object e)好。

Object peek():获取队列头部元素,但是不删除该元素,如果该队列为空则返回null。

Object poll():获取队列的头部元素并删除该元素,如果队列为空则返回null。

Object remove():获取队列的头部元素并删除该元素。


Queue接口有PriorityQueue实现类,有Deque接口,Deque代表一个双端队列,双端队列可以同时从两端来添加和删除元素,因此Deque实现类既可以当做队列使用,也可以当做栈来使用。Java为Deque提供了ArrayDeque和LinkedList两个实现类。

package Suanfa;
import java.util.*;

public class Collection6 {
	public static void main(String[] args) {
		PriorityQueue pq1=new PriorityQueue();
		pq1.offer(6);
		pq1.offer(12);
		pq1.offer(-3);
		System.out.println(pq1);//[-3,12,6]
		System.out.println(pq1.poll());//[-3]
	}
}

PriorityQueue是一个比较标准的队列实现类,不是按照队列元素的添加顺序,而是按照队列元素的大小进行重新排序。因此当调用peek()方法或者poll()方法取出队列的元素并不是取出最先进入的元素,而是最小的元素。

Deque接口与ArrayDeque实现类

双端队列,能够实现栈和队列的功能。

LinkedList实现类

LinkedList实现了List和Deque接口,可以根据索引来随机的访问集合中的元素。可以被当做双端队列来使用,因此可被当做队列也可被当做栈来使用。

LinkedList与ArrayList、ArrayDeque的实现机制完全不同,ArrayList、ArrayDeque内部是以数组的形式保存集合中元素,而LinkedList内部以链表的形式保存集合元素。

各种线性表的性能分析

如果需要遍历List集合元素:对于ArrayList、Vector集合,应该使用随机访问方法(get)来遍历集合元素,这样性能更好;对于LinkedList集合,应该使用迭代器(Iterator)来遍历集合元素。

如果需要经常执行插入、删除操作来改变包含大量数据的List集合大小,可考虑使用LinkedList集合。

如果有多个线程需要同时访问List集合中的元素,可以使用Collections类将集合包装成线程安全的集合。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值