java 集合详解

数组

定义一个数组
格式:数据类型[] 数组名 = new 数据类型[长度];
格式:数据类型[] 数组名 = new 数据类型[]{值1,值2,…,值n};
格式:数据类型[] 数组名 = {值1,值2,…,值n};

集合

 集合的概念:
 * 
 * 1.数组的弊端:
 * 		1).长度是固定的,后期不能改变;
 * 		2).数组一旦定以后,其类型就固定了,只能存储此类型数据;
 * 2.Java为我们提供了一些类,这些叫:集合类。这些集合类功能类似于"数组",
 *   为程序员做"容器"用的。
 * 3.集合的好处:
 * 		1).我们程序员使用集合时,可以不用关心"长度"信息,我们只需要往里面存东西,或者
 *         从内部删除元素

体系结构

1.体系结构:
		Collection(接口):单列集合
			|--List(接口):1.有序;2.可以存储重复值
				|--ArrayList(类):数组实现;线程不安全(不同步),效率高;
				|--Vector(类):数组实现;线程安全的(同步),效率低;
				|--LinkedList(类):链表;线程不安全的(不同步),效率高;
			|--Set(接口):1.无序的;2.不能存储重复值
				|--HashSet(类):哈希表;
					保证元素唯一性:hashCode()和equals()
				|--LinkedHashSet(类):链表、哈希表;(特例:有序的)
					由链表保证顺序;
					由哈希表保证唯一性;
				|--TreeSet(类):树;
					对元素排序和保证元素唯一性:
					1.自然排序:
						1).自定义类,实现:Comparable接口;
						2).重写compareTo()方法;
					2.比较器排序:
						1).自定义比较器,实现:Comparetor接口;
						2).重写compare()方法;
						3).实例化TreeSet时,将自定义比较器作为参数传递给TreeSet的构造方法;

		Map(接口):双列集合
			|--HashMap(类):键:哈希表结构
			|--LinkedHashMap(类):键:链表、哈希表结构;
			|--TreeMap(类):键:树;
			|--Hashtable(类):键:哈希表结构

	HashMap和Hashtable的区别:
		1.HashMap可以存储null键和null值;
		  Hashtable不能存储null键和null值;
		2.HashMap线程不安全的(不同步);
		  Hashtabel线程安全的;
	2.将数组转换为集合:Arrays-->asList()
	   它的返回值Arrays的内部类:ArrayLisst
	   对于此返回值:
		不能对集合元素:新增、删除;
		可以对集合元素:修改;
	3.数据结构:
		1).数组:查询快;增删慢;
		2).链表:查询慢;增删快;
		3).哈希表:综合了数组和链表的优点,查询、增、删都很快;关键取决于哈希算法;
		4).栈:先进后出;
		5).队列:先进先出;
		6).树:小的存到左边,大的存到右边,相同的不存;
	4.遍历的方式:
		Collection
			1).toArray();
			2).iterator();
			3).增强for();
			|--List:
				4).ListIterator:
				5).Collection的size()和List的get()
			|--Set:
				(无)
		Map
			1).keySet():键集合;
			2).entrySet():键值对的集合;

Collection

它是List和Set的顶层接口;

1.基本方法:
 * 		boolean add(Object e):向集合中添加一个元素
		boolean remove(Object o):从集合中移除一个元素
		void clear():清空集合
		boolean contains(Object o):判断集合中是否包含参数对象
		boolean isEmpty():判断集合是否为空
		int size():获取集合内元素的数量
	2.批量的方法:
		boolean addAll(Collection c):将参数集合加入到当前集合中;
		boolean removeAll(Collection c):移除此 collection 中那些也包含在指定 collection 中的所有元素
		boolean containsAll(Collection c):如果此 collection 包含指定 collection 中的所有元素,则返回 true。
		boolean retainAll(Collection c):移除此 collection 中未包含在指定 collection 中的所有元素。 
	3.遍历的方法:
		1.Object[] toArray():将集合中的元素转换为Object[]数组
		2.Iterator iterator():迭代器

List

List(接口):1.有序的;2.可以存储重复元素
特有功能:
 * 			void add(int index,Object element):将element添加到index位置,原index位置上的元素,及后续元素全部后移;
			Object remove(int index):移除index位置上的元素;
			Object get(int index):获取index位置上的元素;
			Object set(int index,Object element):将index位置上的元素替换为参数的element元素
			遍历的方法:
			1.ListIterator listIterator():
			2.结合Collection的size(),和本接口的get(int index)方法,可以使用for循环遍历;
//3.遍历集合
		//方式一:ListIterator()
		ListIterator listIt = list.listIterator();
		while(listIt.hasNext()){
			System.out.println(listIt.next());
		}
		System.out.println("向前遍历:");
		while(listIt.hasPrevious()){
			System.out.println(listIt.previous());
		}
		//方式二:结合Collection的size(),和本接口的get(int index)方法,可以使用for循环遍历;
		for(int i = 0;i < list.size() ; i++){
			System.out.println(list.get(i));
		}
 并发修改异常:
 * 
 * 1.产生原因:通常是当我们使用"迭代器"遍历集合时,通过"集合对象"去修改集合中的元素,
 *          这时,就会产生并发修改异常;
 * 2.解决方式:
 * 			通过迭代器遍历,就通过迭代器去修改;
 */

自定义一个List

public class MyArrayList {
	private Object[] objArray = new Object[2];
	private int index = 0;
	//模拟add()方法
	public void add(Object obj){
		objArray[index] = obj;
		index++;
		//判断数组是否已满,如果满,就扩容
		if(index == objArray.length){
			//扩容
			//1.定义新数组
			Object[] newArray = new Object[objArray.length * 2];
			//2.将原数组中的元素复制到新数组
			for(int i = 0;i < objArray.length ; i++){
				newArray[i] = objArray[i];
			}
			//3.将原数组丢弃
			objArray = newArray;
		}
	}
	
	//模拟size()方法:返回当前元素的数量
	public int size(){
		return index;
	}
	
	//模拟get(int index)方法获取元素
	public Object get(int idx){
		if(idx >= this.index){
			return null;
		}
		return objArray[idx];
	}
	
	
	
}

链表
在这里插入图片描述
java 自定义一个链表

public class MyLinkedList {
	//存储首链
	private Box firstBox = null;
	//记录元素数量
	private int count = 0;
	
	//模拟add()方法
	public void add(Object obj){
		//1.实例化一个Box
		Box box = new Box();
		box.obj = obj;
		//计数器
		count++;
		//2.判断是否是首链
		if(this.firstBox == null){
			this.firstBox = box;
		}else{
			//3.遍历,找到最后一个链
			Box tempBox = this.firstBox;
			while(tempBox.next != null){
				tempBox = tempBox.next;
			}
			//4.此时tempBox就是最后一环,将box连接上去
			tempBox.next = box;
		}
	}
	
	//模拟size()方法
	public int size(){
		return this.count;
	}
	//模拟get()方法
	public Object get(int index){
		if(index >= count){
			return null;
		}
		//从首链开始找,并且开始计数
		int n = 0;
		Box tempBox = this.firstBox;
		while(tempBox.next != null && n != index){
			tempBox = tempBox.next;
			n++;
		}
		return tempBox.obj;
	}
	
	private class Box{
		public Object obj;
		public Box next;
	}
}

List三个子类的特点

|--List(接口):1.有序;2.可以存储重复值;
 * 			|--ArrayList(类):数组实现;线程不安全的(不同步的),效率高;
 *  						(无特有成员方法,全部使用父类的)
 * 			|--Vector(类):数组实现;线程安全的(同步的),效率低;
 * 						特有成员:
 * 						public void addElement(Object obj):将元素obj添加到集合;
						public Object elementAt(int index):获取index位置上的元素;
 * 			|--LinkedList(类):链表实现;线程不安全的(不同步的),效率高;
 * 						特有成员:
						public void addFirst(E e)及addLast(E e)
						public E getFirst()及getLast()
						public E removeFirst()及public E removeLast()

LinkedList模拟栈数据结构的

public class MyLinkedList {
	private LinkedList list = new LinkedList();
	
	public void add(Object obj){
		//压栈
		this.list.addFirst(obj);
	}
	
	public int size(){
		return list.size();
	}
	
	public Object getFirst(){
		Object obj = list.getFirst();
		//移除第一个元素
		list.removeFirst();
		return obj;
	}
}

增强for的概述和使用

/*
 * 增强for的概述和使用
 * 
 * 1.增强for(也叫:foreach循环)可以很简便的语法遍历:数组、集合
 * 2.例如遍历数组:编译后,被编译成"普通for循环"
 * 		int[] intArray = {1,43,432,432,43};
 * 		for(int n : intArray){
 * 			System.out.println(n);
 * 		}
 * 	 遍历集合:编译后,被编译为"迭代器的方式"
 * 		ArrayList<String> strList = new ArrayList<>();
 * 		strList.add("aaa");
		strList.add("bbb");
		strList.add("ccc");
		strList.add("ddd");
		
		for(String s : strList){
			System.out.println(s);
		}
	3.增强for特点:
		1).在使用增强for时,没有使用"循环变量";
		2).所以,如果在循环过程中需要使用循环变量,那么还得使用普通for循环;
		        如果不需要循环变量,只是简单的从头遍历到结尾,那么可以考虑使用"增强for";
 */

可变参数:

/*
 * 可变参数:
 * 
 * 1.在定义方法时,如果不确定需要的某种类型的形参有多少个,这时可以使用"可变参数";
 *   例如:public static int sum(int ... nums){
 *   	}
 * 2.可变参数,在方法内部,按照"数组"的方式去处理;
 * 3.在方法形参列表中,可变参数可以跟其它普通参数共存,但"可变参数"要位于形参列表的末尾;
 * 4.调用时,对于"可变参数"的"实参"可以没有,也可以有多个;
 */

set

|--Set(接口):1.无序的;2.不能存储重复元素;
			|--HashSet(类):哈希表实现;
				保证元素唯一性:元素的hashCode()和equals()
			|--LinkedHashSet(类):链表、哈希表实现(特例:有序的)
				由链表保证顺序;
				由哈希表保证元素唯一;
			|--TreeSet(类):树实现;
				排序和保证元素唯一性:
					1.自然排序:
						1).要存储的元素实现:Comparable接口
						   重写compareTo()方法;
						   "a".compareTo("b"):负数:存到左边
						                      0:不存;
								      正数:存到右边
					2.比较器:
						1).要存储的元素无需实现任何接口;
						2).自定义比较器,实现:Comparator接口
						   重写:compare()方法
						3).在实例化TreeSet时,将自定义比较器传递给TreeSet的构造方法;

HashSet

/*
 * 当使用HashSet存储自定义对象时,我们要重写hashCode()和equals()方法;
 * 
 * 因为:HashSet内部的add()方法,先判断hashCode()是否相同,如果相同,再判断equals(),如果equals()也相同:不存;
 * 
 * 源码:if(e.hash == hash && ((k = e.key) == key || key.equals(k))){
 * 	   }
 *    
 */
public class Demo {
	public static void main(String[] args) {
		//1.实例化一个Set
		Set<Student> stuSet = new HashSet<>();
		//2.填充元素
		stuSet.add(new Student("刘德华",20));
		stuSet.add(new Student("张学友",22));
		stuSet.add(new Student("黎明",24));
		stuSet.add(new Student("郭富城",26));
		//添加重复元素
		stuSet.add(new Student("郭富城",26));
		
		//3.遍历
		for(Student stu : stuSet){
			System.out.println(stu.name + "," + stu.age);//取出时,跟存入的顺序不同;
		}
	}
}
public class Student {
	String name;
	int age;
	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + age;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Student other = (Student) obj;
		if (age != other.age)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		return true;
	}
	
	
}

哈希表
在这里插入图片描述
HashSet源码,其实用的是HashMap

class HashSet{
	private HashMap map = new HashMap();
	public void add(Object obj){
		map.put(obj, new Object());
	}
}
class HashMap{
	public void put(Object key , Object v){
		//......
		int hash = hash(key);//生成哈希值
	}
}

TreeSet

/*
 * 当使用TreeSet存储自定义对象时:
 * 		1.实现"自然排序":
 * 			1.要存储的元素实现Comparable接口;
 * 			2.并重写compareTo()方法;
 * 		或者
 * 		2.实现"比较器":
 * 			1.要存储的元素,无需实现任何接口;
 * 			2.自定义比较器类,实现:Comparator接口;
 * 			     重写:compare()方法;
 * 			3.在实例化TreeSet时,将自定义的比较器传给TreeSet构造方法;
 */

自然排序

public class Student implements Comparable<Student>{
	String name;
	int age;
	public Student(String name, int age) {
		super();
		this.name = name;
		this.age = age;
	}
	@Override
	public int compareTo(Student o) {
		System.out.println("this.name = " + this.name + " o.name = " + o.name);
		//1.先按姓名排序
		int n1 = this.name.compareTo(o.name);
		//2.如果姓名相同,按年龄排序
		int n2 = (n1 == 0 ? this.age - o.age : n1);
		return n2;
	}
}

比较器排序

public class Demo {
	public static void main(String[] args) {
		//1.实例化一个TreeSet
		TreeSet<Student> stuSet = new TreeSet<>(new MyComparator());
		stuSet.add(new Student("zhangxueyou",20));
		stuSet.add(new Student("liudehua",22));
		stuSet.add(new Student("zhangziyi",24));
		
		for(Student stu : stuSet){
			System.out.println(stu.name + "," + stu.age);
		}
	}
}
class MyComparator implements Comparator<Student>{
	@Override
	public int compare(Student o1, Student o2) {
		System.out.println("o1.name = " + o1.name + " o2.name = " + o2.name);
		//1.先按姓名比
		int n1 = o1.name.compareTo(o2.name);
		//2.如果姓名相同,比较年龄
		int n2 = (n1 == 0 ? o1.age - o2.age : n1);
		return n2;
	}
	
}

树结构
在这里插入图片描述

Map

* Map(接口):双列集合:在存储时,同时指定两个字段;一个做"键",一个做"值"(键值对);
 * 	|--HashMap:哈希表结构:
 * 	|--LinkedHashMap:链表、哈希表结构:
 * 		 1.有序的;由链表保证顺序;
 * 		 2.由哈希表保证元素唯一;
 * 	|--TreeMap:树结构:
 * 以上Map接口的各种"数据结构"全部是针对"键"有效,跟"值"无关;

Map接口的基本功能:

Map接口的基本功能:
 * 
 * 注:以下方法中标示为:K的表示:键,V的表示:值
 * 
 * 	V put(K key,V value) :向集合中添加"键值对",如果发生重复的键,将用新值替换原值,并将原值返回;
	V remove(Object key):移除key所指定的"键值对"
	void clear():清空集合
	boolean containsKey(Object key):判断是否包含指定key
	boolean containsValue(Object value):判断是否包含指定的value
	boolean isEmpty():判断集合是否为空
	int size():集合中"键值对"的数量
* Map的获取功能:
 * 	V get(Object key):使用指定key查找对应"值"
	Set<K> keySet():获取所有的"键"的集合(Set集合)
	Collection<V> values():获取所有"值"的集合(Collection集合)
	Set<Map.Entry<K,V>> entrySet():返回所有的"键值对-对象";Entry是Map的内部类;
	                               	一个Entry内部封装了一个"键"和一个"值"
* Map的遍历方式:
 * 
 * 1.keySet():获取所有"键"的集合,然后遍历键集合;并结合get(K key)方法获取相应的值;
 * 2.entrySet():获取所有"键值对"的集合;	  
 *                              	

"aababcabcdabcde",获取字符串中每一个字母出现的次数
要求结果:
a(5)b(4)c(3)d(2)e(1)

public class Demo {
	public static void main(String[] args) {
		//1.定义字符串
		String str = "aababcabcdabcde";
		//2.准备一个TreeMap
		TreeMap<Character,Integer> map = new TreeMap<>();
		//3.遍历字符串,取出每个字符
		for(int i = 0 ;i < str.length() ; i++){
			char c = str.charAt(i);
			//4.用"字符"做键,到map中查找相应的值
			Integer value = map.get(c);
			//5.value有两种情况:1).null,之前没存过;2).非null,之前已经存过了
			if(value != null){
				value++;
				//用字符做键,新的value做值,再次添加到集合中;将会用新值替换原值
				map.put(c, value);
			}else{
				map.put(c, 1);
			}
		}
		//封装结果字符串
		StringBuffer buf = new StringBuffer();
		//遍历集合
		Set<Character> keys = map.keySet();
		for(char c : keys){
			buf.append(c).append("(").append(map.get(c)).append(")");
		}
		System.out.println(buf);
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值