Java基础(八)——集合类

Collection集合

	集合是存储引用类型的容器,长度可变
	对于基本类型,有对应的引用类型的自动转换
	集合的顶级接口为Collection接口
	(简略)
	Collection接口的子接口(继承):List接口, Set接口
	List接口的子类(实现):ArrayList类,LinkedList类
	Set接口的子类(实现):HashSet类,LinkedHashSet类
	
	List(列表),Set(集合)
	ArrayList(数组列表),LinkedList(链表)
	HashSet(哈希表),LinkedHashSet(基于链表的哈希表)

Collection常用方法

collection接口主要方法:

boolean add(Object o)添加对象到集合
boolean remove(Object o)删除指定的对象
int size()返回当前集合中元素的数量
boolean contains(Object o)查找集合中是否有指定的对象
boolean isEmpty()判断集合是否为空
Iterator iterator()返回一个迭代器
boolean containsAll(Collection c)查找集合中是否有集合c中的元素
boolean addAll(Collection c)将集合c中所有的元素添加给该集合
void clear()删除集合中所有元素
void removeAll(Collection c)从集合中删除c集合中也有的元素
void retainAll(Collection c)从集合中删除集合c中不包含的元素
Object[] toArray()	将集合转为数组

Iterator迭代器

迭代器是遍历集合的一种方式,是依赖集合存在的,是一个接口
集合类中有一部分是没有索引的,无法根据索引来取值
方法:
	boolean hasNext();判断是否还有可以取出的元素
	 next();取出下一个元素
collection接口中有一个Iterator(),获取一个迭代器的实现对象	
所以collection接口的子类也有这个方法

迭代器的并发异常:当迭代器进行遍历集合元素时,依靠集合自身的方法修改
				集合的长度,会发生异常
List集合可以使用自身迭代器的添加元素的方法,在遍历的同时完成集合长度的修改
或者用for遍历同时添加元素

迭代器为什么是一个接口而不是一个类?
集合类中的数据结构是不同,所以遍历方式也是不同的
List<Integer> lists= new ArrayList<Integer>();
List.add(1);
List.add(2);
Iterator<Integer> iterator = lists.iterator();//获取迭代器
while (iterator.hasNext()) {//判断迭代器中是否有元素
	System.out.println(iterator.next());//取出元素
	//lists.add(1);遍历同时修改集合长度会发生并发异常
	//删除元素可以使用Iterator的remove()方法
	//JDK1.8开始,可以使用removeIf()方法来代替Iterator的remove()方法实现遍历的同时删除元素
	//element = iterator.next()
	//lists.removeIf(element  -> "abc".equals(element ));
}

泛型

Collection<Integer>
泛型能约束集合中能存储的类型,将运行期可能出现的类型错误转移到编译期
未指定泛型,存储的就是object类型,就无法使用子类特有的方法了
取值时可能会有类型安全的问题	
泛型的通配符<?>
//迭代任意集合类型的元素
public static void getIterator(Collection<?> coll) {
	Iterator<?> iterator = coll.iterator();
	while (iterator.hasNext()) {
		System.out.println(iterator.next());
	}
}

List

Collection集合的子接口,以索引的方式记录所添加的对象顺序

特点是有序(存储和取出的顺序一致),可存储重复元素,有索引
特有功能:
	void add(int index,Object element)在指定位置添加元素
	Object get(int index)获取指定位置的元素
	ListIterator listIterator() List集合特有的迭代器
	Object remove(int index)根据索引删除指定的元素,并返回删除的元素
	Object set(int index,Object element)根据索引修改元素,返回被修改的元素
	Arrays工具类的static List <T> asList()方法:将数组转换为集合,
							不能修改此集合长度,本质还是一个不可变长度的数组
List<String> list = new ArrayList<String>();
list.add(0, "0");
list.add(1, "1");
list.add(1, "2");//若该索引已经有元素,原元素后移一位,放入新元素
list.add(2, "3");
ListIterator<String> listIterator = list.listIterator();
while (listIterator.hasNext()) {
	System.out.print(listIterator.next() + " ");
	// 0 2 3 1
}

ArrayList

List接口的一个实现类
本身是一个数组,元素增删慢,查找快,线程不安全但是效率高,是最为常用的集合
add():添加元素
get():获取元素
size():获取集合长度
//public class ArrayList<E> extends AbstractList<E>
//       implements List<E>, RandomAccess, Cloneable, java.io.Serializable
ArrayList<Integer> arrayList = new ArrayList<Integer>();
arrayList.add(1);
arrayList.add(2);
arrayList.add(3);
for (int i = 0; i < arrayList.size(); i++) {
	System.out.println(arrayList.get(i));
}

LinkedList

List接口的一个实现类
底层数据结构是链表,查询慢,增删快,线程不安全,效率高
addFirst():将元素添加到链表第一位,可以用于模拟栈的后进先出
addLast():将元素添加到链表最后一位,意义不大
getFirst():获取链表第一位元素
getLast():获取链表最后一位元素
removeFirst():移除并返回链表的第一个元素
removeLast():移除并返回链表的最后一个元素
isEmpty():如果链表不包含元素,返回true

Vector

List接口的一个实现类
底层数据结构是数组,查询快,增删慢
线程安全,效率低
addElement()添加元素
elementAt(int i)获取元素
elements()返回一个枚举对象用于遍历

Set

collection接口的子接口
不存储重复元素,无序,没有索引,常用于去重操作
只能使用增强for或者迭代器遍历元素
JDK8的HashSet实现变了,导致元素输入与输出不一定无序
HashSet可以保证元素的输出顺序与元素的输入顺序一致
TreeSet可以保证元素的自然排序(与元素的输入顺序无关)

HashSet

set接口的实现类,HashSet可以保证元素的输出顺序与元素的输入顺序一致
底层数据结构是哈希表
增删快,线程不安全,效率高
HashSet元素唯一性保证--其底层是HashMap,依赖两个方法:hashCode()和equals()
HashSet的add方法实质是调用HashMap的put方法
Set<String> set = new HashSet<String>();
		set.add("a");
		set.add("b");
		set.add("c");
		set.add("d");
		set.add("e");
		set.add("f");
		for (String n : set) {
			System.out.print(n + " ");
			//a b c d e f  输出顺序与输入顺序一致
		}

LinkedHashSet

HsahSet的子类,基于链表的哈希表实现
底层数据结构是链表和哈希表
由链表保证元素有序
由哈希表保证元素唯一
线程不安全,效率高

判断元素是否重复

contains()实质是利用equals方法与集合内元素做比较,
			对于自定义类型,需要重写equals方法
Set集合的add():利用equals方法和hashCode()完成的,对于自定义类型元素,需要重写equals方法和hashCode()

TreeSet

TreeSet是SortedSet接口的唯一实现(SortedSet接口继承Set接口)
能够对元素按照某种规则进行排序
其底层是二叉树结构
TreeSet 是一个有序的集合
TreeSet是非线程安全的
TreeSet中的元素支持2种排序方式:自然排序 或者 
			根据创建TreeSet 时提供的 Comparator 进行排序
自然排序如int,String的排序
自定义类添加进TreeSet需要实现Comparable接口
重写compareTo方法
//自定义数据类型,并在自定义的数据类型中实现CompareTo方法  
class Person implements Comparable {  
    String name;  
  
    Person (String name) {  
        this.name = name;  
    }  
  
    public String toString() {  
        return " 姓名:" + name;  
    }  
  
    public int compareTo(Object o) {  
        Person ss = (Person) o;  
        int result = name.compareTo(ss.name);    
        return result;  
    }  
}  
  
public class TreeSetTest {  
    public static void main(String[] args) {          
        Set<Person > treeSet = new TreeSet<Person>();  
        treeSet.add(new Person ("zhangsan"));  
        treeSet.add(new Person ("lisi"));  	
        System.out.println(treeSet);//[ 姓名:lisi, 姓名:zhangsan]
        // 在自定义类Person中重写compareTo方法,使用String类的compareTo方法来完成排序
	
    }   
}

集合的数据构造特点

集合存储元素的数据结构是不同的
堆栈:元素是先进后出
		栈的入口和出口都在最顶端的位置
队列:元素是先进先出
数组:查询快,通过索引快速访问指定位置的元素
	增删慢,
		在指定位置上添加元素,需要创建一个新数组,指定新元素存储在新数组
			的指定位置,再将原数组元素根据索引复制到新数组对应索引的位置上
		指定索引位置删除元素,需要创建一个新数组,将原数组元素根据索引
			复制到新数组对应索引的位置上,原数组指定要删除的索引位置元素不复制
链表:多个节点间通过地址连接,
	每个节点至少包括两个部分:
			一个是存储数据元素的数据域,另一个是存储下一个节点地址
	查找元素慢
	增删元素快

equals与hashCode

equals 默认调用object中的方法,比较的是内存地址
equals 重写后一般是根据内容判断对象是否相等,String就重写了equals		

hashCode	在对象的内存地址基础上经过特定算法返回一个hash值
哈希值的作用是确定该对象在哈希表中的索引位置
set集合的元素不重复实质是调用了HashMap的put方法
(简略)判断hash值是否一致,hash值不一致就添加元素
hash值一致就调用equals,判断对象是否相等
不相等,添加元素

一个类重写equals方法(比较内容是否相等)不重写hashCode方法
	当对象的equals为true时,hash值不一定相等
一个类重写hashCode不重写equals方法方法
	当对象的hash值相等时,对象的equals方法不一定为true
一个类重写equals方法和hashCode方法
	当对象的equals为true时,hash值一定相等
	当对象的hash值相等时,equals不一定为true
	
当对象的equals返回true时,这两个对象不一定是同一个对象
当对象的hash值相等时,这两个对象也不一定是同一个对象
当一个类要用到set集合的去重功能时,这个类一定要重写equals和hashCode方法

Map集合

Map是一个顶级接口,与collection接口无关
Map集合中元素是以键值对的形式存在,key-value一一映射的,key和value的数据类型可以不一样
key不可以重复,value可以重复
Map集合的数据结构只跟键有关,跟值无关

Map中有泛型<K,V>决定key和value的数据类型,两者的类型可以不一样
常用的实现类是HashMap和LinkedHashMap
常用方法:
put(k,v) 添加元素;当key已存在时,会覆盖原先的value值
get(k) 通过key获取值,没有返回null
remove(k) 通过key来删除键值对
keySet() 返回所有key组成的Set集合

HashMap

Map接口的实现类,最常用的Map集合类型
HashMap底层是数组+链表+红黑树(JDK1.8增加了红黑树部分)实现的
存储的数据采用的哈希表的结构,不能保存元素存取顺序的一致,只能保证内部元素的自然排序(根据key自然排序)
要保证key的唯一性,key的类型需要重写equals和hashCode方法
允许使用 null 值和 null 键
线程不安全的,效率高
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("张三", 23);
map.put("李四", 24);
map.put("王五", 25);
//字符串中文的自然排序lisi > zhangsan > wangwu 
System.out.println(map);//{李四=24, 张三=23, 王五=25}
// 遍历
Set<String> s = map.keySet();// 返回所有key组成的Set集合
for (String string : s) {
	Integer value = map.get(string);
	System.out.println(string + " : " + value);
		// 李四 : 24
		// 张三 : 23
		// 王五 : 25
}

LinkedHashMap

HashMap的子类,元素的输出顺序与输入顺序一致
存储结构采用哈希表 + 链表
由链表保证元素有序
由哈希表保证元素唯一
要保证key的唯一性,key的类型需要重写equals和hashCode方法
Map<String, Integer> map = new LinkedHashMap<String, Integer>();
map.put("张三", 23);
map.put("李四", 24);
map.put("王五", 25); // 元素的输出顺序与输入顺序一致
System.out.println(map);// {张三=23, 李四=24, 王五=25}

Hashtable

实现了Map接口,用法与HashMap一致
线程安全的,效率低
不允许null值,null键
子类为Properties类,Properties常配合IO使用

JDK1.5引入静态导入

import static java.lang.System.out;//最后的必须是一个成员
public static void main(String[] args) {
		out.print("abc");//好处是可以少写一个System,不常用
}

JDK1.5引入可变参数
类型…变量名
1.一个方法只能有一个可变参数
2.可变参数必须写在其他参数的后面

public static void main(String[] args) {
		System.out.println(add(1, 2, 3));
		System.out.println(add(2, 3, 5, 6, 8, 7));
	}

	public static int add(int... i) {
		int sum = 0;
		for (int j : i) {
			sum += j;
		}
		return sum;
	}

Collections工具类

提供操作集合的方法的工具类
以下方法对List进行操作
void reverse(List list),反转元素
void shuffle(List list),随机排序
void sort(List list),按自然排序的升序排序
void sort(List list, Comparator c),定制排序,由Comparator控制排序逻辑
void swap(List list, int i , int j),交换两个索引位置的元素
int binarySearch(List list, Object key), 对List进行二分查找,返回索引,注意List必须是有序的
int max(Collection coll),根据元素的自然顺序,返回最大的元素
int indexOfSubList(List list, List target), 统计targe在list中第一次出现的索引,找不到则返回-1
boolean replaceAll(List list, Object oldVal, Object newVal), 用新元素替换旧元素
List<String> list = new ArrayList<String>();
list.add("h");
list.add("j");
list.add("f");
list.add("t");
list.add("u");
System.out.println(list);// [h, j, f, t, u]
Collections.sort(list);// 对list集合排序
System.out.println(list);// [f, h, j, t, u]
Collections.shuffle(list);// 随机打乱list集合元素的顺序
System.out.println(list);// [h, f, j, u, t]

例子

package pokers;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.TreeSet;

/**
 * 斗地主: 
 * 1:创建54张扑克牌,发给3个玩家,2个字符串数组分别装花色和数字,组成扑克牌牌面值 
 * 2.将扑克牌装入一个Map集合中,下标 0 - 53作为key(包括大小王 )
 * 3.将对应的key装入另一个list集合中(便于查询和洗牌) 
 * 4.随机排列list集合Collections.shuffle(array) 
 * 5.将集合发给3个玩家(set集合,便于增删),剩余最后3张存入底牌(set集合)
 * 6.调用显示方法,传入3个玩家手牌(set集合,排序)并显示
 * 7.显示3个玩家的手牌 + 底牌,根据传入的4个集合,排序显示
 * 
 * @author Administrator
 *
 */
public class Poker {

	public static void main(String[] args) {
		// 存放扑克的Map集合
		HashMap<Integer, String> map = new HashMap<Integer, String>();
		// 存放编号的List集合,用这个编号完成洗牌操作
		ArrayList<Integer> arrayList = new ArrayList<Integer>();

		String[] colors = { "♠", "♥", "♣", "♦" };

		String[] numbers = { "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2" };
		// 编号
		int index = 0;
		for (String numberString : numbers) {// 先数字再花色,便于排序输出
			for (String colorString : colors) {
				String pokerString = colorString.concat(numberString);
				map.put(index, pokerString);//存入编号与扑克牌牌面
				arrayList.add(index);//存入编号
				index++;
			}
		}
		map.put(index, "小王");
		arrayList.add(index);
		index++;
		map.put(index, "大王");
		arrayList.add(index);
		// 以上扑克就准备完毕

		// 定义三个玩家的手牌区与底牌区
		//TreeSet中存入的元素是有序的(是自然排序不是输入输出顺序),不需要发完牌后的排序操作
		TreeSet<Integer> user1 = new TreeSet<Integer>();
		TreeSet<Integer> user2 = new TreeSet<Integer>();
		TreeSet<Integer> user3 = new TreeSet<Integer>();
		TreeSet<Integer> card = new TreeSet<Integer>();

		// 打乱顺序,洗牌
		Collections.shuffle(arrayList);
		//发牌
		for (int i = 0; i < arrayList.size(); i++) {
			if (i >= (arrayList.size() - 3)) {
				card.add(arrayList.get(i));
			} else if ((i % 3) == 0) {
				user1.add(arrayList.get(i));
			} else if ((i % 3) == 1) {
				user2.add(arrayList.get(i));
			} else if ((i % 3) == 2) {
				user3.add(arrayList.get(i));
			}
		}
		//看牌
		toPoker("张三", user1, map);
		toPoker("李四", user2, map);
		toPoker("王五", user3, map);
		toPoker("底牌", card, map);
		/**
		 * TreeSet集合具有自动排序功能,1.让元素自身具备比较性。只要让元素实现Comparable接口,compareTo方法即可。 2.自定比较器的方式。
		 * 这时可以让集合自身具备比较性。 TreeSet<Integer> test = new TreeSet<Integer>(); test.add(85);
		 * test.add(55); test.add(45); test.add(65); System.out.println(test);
		 */

	}

	public static void toPoker(String name, TreeSet<Integer> ts, HashMap<Integer, String> map) {
		System.out.println(name + "的牌是: ");
		for (Integer integer : ts) {
			String valueString = map.get(integer);
			System.out.print(valueString + " ");
		}
		System.out.println();

	}

}
// 张三的牌是:
// ♠3 ♥3 ♥4 ♠5 ♥6 ♥7 ♠9 ♣9 ♦9 ♥10 ♥J ♣K ♠A ♥A ♣A ♥2 ♣2
// 李四的牌是:
// ♣3 ♠4 ♦4 ♥5 ♣5 ♠7 ♣7 ♥8 ♣8 ♦10 ♠J ♣J ♦J ♠K ♦K ♦A ♠2
// 王五的牌是:
// ♦3 ♦5 ♣6 ♦6 ♦7 ♠8 ♦8 ♥9 ♠10 ♣10 ♠Q ♥Q ♣Q ♥K ♦2 小王 大王
// 底牌的牌是:
// ♣4 ♠6 ♦Q

总结

Collection(单列集合)
	List(有序,可重复):
		ArrayList(常用):底层数据结构是数组,查询快,增删慢,线程不安全的,效率高
		Vector:底层数据结构是数组,查询快,增删慢,线程安全的,效率低
		LinkedList:底层数据结构是链表,查询慢,增删快,线程不安全的,效率高
	 Set(无序,唯一):JDK1.8后不能保证元素的有序(也不一定无序)
		HashSet:底层数据结构是哈希表,哈希表依赖于两个方法:hashCode()和equals(),
				执行顺序:首先判断hashCode()值是否相同;(是,就执行equals(),
				看其返回值;true,说明元素重复,不添加;false,就直接添加到集合中);
				否,就直接添加到集合中
		LinkedHashSet:底层数据结构是由链表和哈希表组成,
						由链表保证元素的有序,
						由哈希表保证元素的唯一
		TreeSet:底层数据结构是红黑树,
		如何保证元素的唯一性:根据比较的返回值是否为0来决定的
		如何保证元素的排序:两种方式,
				1.自然排序(元素具备比较性)让元素所属的类实现Comparable接口 
				2.比较器排序(集合具备比较性)让接口接受一个Comparator的实现类对象
Map(双列集合):
	1.Map集合的数据结构仅仅针对键有效,与值无关
	2.存储的是键值对形式的元素,键唯一,值可以重复
	
	HashMap:与HashSet的描述一致,线程不安全的,效率高
	LinkedHashMap:与LinkedHashSet描述一致
	Hashtable(不常用):与HashMap描述一致,线程安全的,效率低
	TreeMap:与TreeSet描述一致

该使用那种集合:看需求
是否是键值对形式:
	是:Map
		键是否需要排序
		是:TreeMap
		否:HashMap
		不知道,就使用HashMap
	否:Collection
		元素是否唯一
		是:Set
		              元素是否需要排序
		                                  是:TreeSet
		                                   否:HashSet
		              不知道就使用HashSet
		否:List
		 		要安全吗?
		 		 	是:Vector(一般不用,有替代的)
		  			否:ArrayList或LinkedList
			 				增删多:LinkedList
		  					查询多:ArrayList
		   		不知道就使用ArrayList
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值