JavaSE(六)集合框架

1.vector
通过源码分析,发现在Vector类中有一个Object[]类型数组.
1):表面上把数据存储到Vector对象中,其实底层依然是把数据存储到Object数组中的.
2):我们发现该数组的元素类型是Object类型,意味着集合中只能存储任意类型的对象.
集合中只能存储对象,不能存储基本数据类型的值.
在Java5之前,必须对基本数据类型手动装箱.
如:v.addElement(Integer.valueOf(123));
从Java5开始支持自动装箱操作,代码.
如:v.addElement(123);其实底层依然是手动装箱
//Vector类
底层有两个构造器 , 一个是默认构造器.默认初始容量是10 ;
一个是由我们制定数组容量的构造器
public class VectorDemo {

public static void main(String[] args) {
	Vector v1 = new Vector();// 默认初始容量为10
	v1.add("A");
	v1.add("B");
	v1.add("C");
	v1.add("D");
	System.out.println(v1);
	System.out.println("===========");
	Vector v2 = new Vector(5);// 指定初始容量为5 ,会自动扩容
	StringBuilder sb = new StringBuilder();// 默认初始容量为16
	/*
	 * 集合类中,存储的对象,都是存储对象的引用地址.而不是对象本身
	 * */
	sb.append("abc");
	v2.add(sb); // 存储的是sb的引用地址 ;
	System.out.println(v2);
	sb.append("-seemygo");
	System.out.println(v2);
	System.out.println("===========");

	Vector v3 = new Vector();// 默认初始容量为10
	v3.add(1);
	v3.add(3);
	v3.add(5);
	v3.add("a");
	v3.add("b");
	v3.add("b");
	System.out.println(v3);
	v3.remove(Integer.valueOf(3) );
	//v3.remove("3");
	System.out.println(v3);




}

在这里插入图片描述

在这里插入图片描述
2.LinkList
LinkedList类实现单向队列和双向队列的接口,自身提高了栈操作的方法,链表操作的方法.
在LinkedList类中存在很多方法,但是功能都是相同的.LinkedList表示了多种数据结构的实现,每一种数据结构的操作名字不同.

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

//LinkedList类  实现单向队列和 双向队列的接口
public class LinkedListDemo {
	public static void main(String[] args) {	
		LinkedList list1 = new LinkedList();
		list1.addLast("b");
		list1.addLast("c");
		list1.addLast("5");
		list1.addLast("d");
		list1.addLast("6");
		list1.addLast("8");
		System.out.println(list1);
		list1.addFirst("a");
		System.out.println(list1);

		LinkedList list2 = new LinkedList();
		list2.addFirst("x");
		list2.addLast("y");
		list2.addLast("z");
		System.out.println(list2);
		
		//list1.addAll(list2);//将2的元素全部添加到1的结尾
		//System.out.println(list1);
		//list1.addAll(2,list2);//将2的元素从指定的位置开始添加
		//System.out.println(list1);
		System.out.println(list1.element());//获取第一个元素
		System.out.println(list1.indexOf("c"));//元素首次出现的索引
		System.out.println(list1.pop());//从列表的堆栈弹出一个元素
		System.out.println(list1);
		Object[] arr  =  list1.toArray();//返回列表中所有元素的数组
		System.out.println(Arrays.toString(arr));
		
		
		
		
		
		
	}
}

在这里插入图片描述
在这里插入图片描述
3.Link类总结
List实现类特点和性能分析:
三者共同的特点(共同遵循的规范):
1):允许元素重复.
2):记录元素的先后添加顺序.
Vector类: 底层才有数组结构算法,方法都使用了synchronized修饰,线程安全,但是性能相对于ArrayList较低.
ArrayList类: 底层才有数组结构算法,方法没有使用synchronized修饰,线程不安全,性能相对于Vector较高.
ArrayList现在机会已经取代了Vector的江湖地位.
为了保证ArrayList的线程安全,List list = Collections.synchronizedList(new ArrayList(…));
LinkedList类:底层才有双向链表结构算法,方法没有使用synchronized修饰,线程不安全.
数组结构算法和双向链表结构算法的性能问题:
数组结构算法: 插入和删除操作速度低,查询和更改较快.
链表结构算法: 插入和删除操作速度快,查询和更改较慢.
使用的选择:
Vector类打死不用!即使要用选ArrayList类.
如果删除和插入操作频繁,应该选择LinkedList类.
如果查询操作频繁,应该使用ArrayList类.
在开发中使用ArrayList较多,根据具体的需求环境来做选择.
4.迭代器


import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;




//Iterator迭代器对象,只能从上往下迭代
public class IteratorDemo {
	public static void main(String[] args) {
		List list = new ArrayList();
		list.add("a");
		list.add("b");
		list.add("c");
		list.add("d");
		list.add("e");
		list.add("f");
		list.add("g");
		System.out.println(list);
		System.out.println("===========");

		// 方式1 ; for循环 输出
		for (int i = 0; i < list.size(); i++) {
			System.out.print(list.get(i));
		}
		System.out.println();
		System.out.println("===========");

		// 方式2 for-each 增强for循环
		for (Object ele : list) {
			System.out.print(ele);
		}
		System.out.println();
		System.out.println("===========");
		// Xxx.iterator() 返回按适当顺序在列表的元素上进行迭代的迭代器。
		// Object next():获取指针的下一个元素,并且移动指针.

		// 方式3 使用while 循环操作迭代器Iterator
		Iterator it = list.iterator();// 返回按适当顺序在列表的元素上进行迭代的迭代器
		while (it.hasNext()) {
			System.out.print(it.next());
		}
		System.out.println();
		System.out.println("===========");

		// 方式4 使用for循环来操作迭代器
		
		
		/**
		 * foreach 操作Iterable的实例底层就是采用该方式
		 */
		for(Iterator it2 = list.iterator();it2.hasNext();){
			System.out.print(it2.next());
		}
		System.out.println();
		System.out.println("===========");

	}
}

集合的迭代操作:
把集合做的元素一个一个的遍历取出来.
迭代器对象:
Iterator: 迭代器对象,只能从上往下迭代.
boolean hasNext(); 判断当前指针后是否有下一个元素
Object next():获取指针的下一个元素,并且移动指针.
ListIterator: 是Iterator接口的子接口,支持双向迭代,从上往下迭代,从下往上迭代.
Enumeration:古老的迭代器对象,现在已经被Iterator取代了. 适用于古老的Vector类.
在这里插入图片描述
在这里插入图片描述
5.泛型
泛型类:直接在类/接口上定义的泛型.
使用泛型:
保证前后类型相同.
List list = new ArrayList();//该List集合中只能存储String类型的元素.
因为前后类型相同,所以从Java7开始,退出泛型的菱形语法<>.
List list = new ArrayList<>();
泛型不存在继承的关系(错误如下).
List list = new ArrayList();//错误的
从此以后,使用集合都得使用泛型来约束该集合中元素的类型.
通过反编译,发现:泛型其实也是语法糖,底层依然没有泛型,而且依然使用强转.

//泛型类中的泛型,只适用于非静态方法
public class Point<T> { // T 表示可以为任意类型,具体类型由调用者决定
	private T x;
	private T Y;

	public T getX() {
		return x;
	}

	public void setX(T x) {
		this.x = x;
	}

	public T getY() {
		return Y;
	}

	public void setY(T y) {
		Y = y;
	}

}

6.泛型的擦除和转换
泛型的擦除:
1):泛型编译之后就消失了(泛型自动擦除);
2):当把带有泛型的集合赋给不带泛型的集合,此时泛型被擦除(手动擦除).

堆污染:
单一个方法既使用泛型的时候也使用可变参数,此时容易导致堆污染问题.
如:在Arrays类中的asList方法: public static List asList(T… a).

import java.util.ArrayList;
import java.util.List;

//泛型的擦除和转换

/**
 *  泛型的擦除:  
       1):泛型编译之后就消失了(泛型自动擦除);
       2):当把带有泛型的集合赋给不带泛型的集合,此时泛型被擦除(手动擦除).
 *
 *
 */
public class genericTypeDemo2 {

	public static void main(String[] args) {
		
		//带有Integer类型的泛型
	List<Integer> list1 = new ArrayList<>();	
	list1.add(123);
		
	//不带泛型的集合
	List list2  = null;
	list2 =list1 ; //此时泛型被擦除
	list2.add("abc");
	
	
	//带 Srting类型的泛型
	List<String> list3 = new ArrayList<>();
	list3 = list2;
	
System.out.println(list3);     //此时可以运行,但是程序存在错误
		
String num = list3.get(0);   //编译不会报错,但是运行出错  java.lang.Integer cannot be cast to java.lang.String

 
	}
}

7.Set
Set集合存储特点:
1):不允许元素重复.
2):不会记录元素的先后添加顺序.
8.HashSet
HashSet是Set接口最常用的实现类,顾名思义,底层才用了哈希表(散列/hash)算法.
其底层其实也是一个数组,存在的意义是提供查询速度,插入速度也比较快,但是适用于少量数据的插入操作.

在HashSet中如何判断两个对象是否相同问题:
1):两个对象的equals比较相等. 返回true,则说明是相同对象.
2):两个对象的hashCode方法返回值相等.

对象的hashCode值决定了在哈希表中的存储位置.
二者:缺一不可.
当往HashSet集合中添加新的对象的时候,先回判断该对象和集合对象中的hashCode值:
1):不等: 直接把该新的对象存储到hashCode指定的位置.
2):相等: 再继续判断新对象和集合对象中的equals做比较.
1>:hashCode相同,equals为true: 则视为是同一个对象,则不保存在哈希表中.
1>:hashCode相同,equals为false:非常麻烦,存储在之前对象同槽为的链表上(拒绝,操作比较麻烦).

对象的hashCode和equals方法的重要性:
每一个存储到hash表中的对象,都得提供hashCode和equals方法,用来判断是否是同一个对象.
存储在哈希表中的对象,都应该覆盖equals方法和hashCode方法,并且保证equals相等的时候,hashCode也应该相等.
List接口: 允许元素重复,记录先后添加顺序.
Set接口: 不允许元素重复,不记录先后添加顺序.
需求: 不允许元素重复,但是需要保证先后添加的顺序.
LinkedHashSet:底层才有哈希表和链表算法.
哈希表:来保证唯一性,.此时就是HashSet,在哈希表中元素没有先后顺序.
链表: 来记录元素的先后添加顺序.
9.TreeSet
TreeSet集合底层才有红黑树算法,会对存储的元素默认使用自然排序(从小到大).
注意: 必须保证TreeSet集合中的元素对象是相同的数据类型,否则报错.
在这里插入图片描述

import java.util.NavigableSet;
import java.util.Set;
import java.util.TreeSet;

//treeset 底层采用红黑树算法,默认对集合对象 按照自然顺序排序,所以要求集合对象的类型要一致2
public class TreeSetDemo {
	public static void main(String[] args) {
		TreeSet<String> s = new TreeSet<>();
		s.add("c");
		s.add("f");
		s.add("g");
		s.add("6");
		s.add("9");
		System.out.println(s);
		s.descendingSet();
		System.out.println(s.first());
		System.out.println(s.descendingSet());//返回set包含元素的逆序视图
		System.out.println(s.headSet("f"));// 返回元素严格小于 参数的一个数组

	}
}

在这里插入图片描述
10.TreeSet的定制排序
覆盖 public int compareTo(Object o)方法,在该方法中编写比较规则.
在该方法中,比较当前对象(this)和参数对象o做比较(严格上说比较的是对象中的数据,比如按照对象的年龄排序).
this > o: 返回正整数. 1
this < o: 返回负整数. -1
this == o: 返回0. 此时认为两个对象为同一个对象.
在TreeSet的自然排序中,认为如果两个对象做比较的compareTo方法返回的是0,则认为是同一个对象.
定制排序(从大到小,按照名字的长短来排序):
在TreeSet构造器中传递java.lang.Comparator对象.并覆盖public int compare(Object o1, Object o2)再编写比较规则.
对于TreeSet集合来说,要么使用自然排序,要么使用定制排序.
判断两个对象是否相等的规则:
自然排序: compareTo方法返回0;
定制排序: compare方法返回0;

import java.util.Comparator;
import java.util.TreeSet;

//在treeset里面如何判断两个元素是否相同 

//需求:设置四个人的名字.年龄. 按照设置的命名字长短或者年龄排序
class Person implements Comparable<Person> {
	String name;
	int age;

	public Person(String name, int age) {
		this.name = name;
		this.age = age;
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", age=" + age + "]";
	}

	// 自定义的编写比较规则 (按照年龄来排序) 自然排序
	public int compareTo(Person p) { // 因为是TreeSet ,所以所有的比较元素必须类型相同
		if (this.age > p.age) {
			return 1;
		} else if (this.age < p.age) {
			return -1;
		} else {
			return 0;
		}
	}

}

public class TreeSetcompareble {
	public static void main(String[] args) {
		TreeSet<Person> s = new TreeSet<>();
		s.add(new Person("猪", 5));
		s.add(new Person("张大猪", 25));
		s.add(new Person("大猪", 15));
		s.add(new Person("二猪", 38));
		s.add(new Person("张智大猪", 35));
		System.out.println(s);
		System.out.println("=================");
		TreeSet<Person> s2 = new TreeSet<>(new NameComparator());
		s2.add(new Person("猪", 5));
		s2.add(new Person("张大猪", 25));
		s2.add(new Person("大猪", 15));
		s2.add(new Person("二猪", 38));
		s2.add(new Person("张智大猪", 35));
		System.out.println(s2);
	}
}

class NameComparator implements Comparator<Person> { // 注意泛型的使用
	// 自定义编写 定制排序的规则.名字长度比较器
	@Override
	public int compare(Person o1, Person o2) {
		if (o1.name.length() > o2.name.length()) {
			return 1;
		} else if (o1.name.length() < o2.name.length()) {
			return -1;
		} else {
			if (o1.age > o2.age) {  //当名字长度相等的时候   比较年龄的大小来排序
				return 1;
			} else if (o2.age <o2.age) {
				return -1;
			} else {
				return 0;
			}
		}
		}
	}

在这里插入图片描述
11.Set接口总结
在这里插入图片描述
Set接口的实现类:
共同的特点:
1):都不允许元素重复.
2):都不是线程安全的类.
解决方案:Set s = Collections.synchronizedSet(Set对象);
HashSet: 不保证元素的先后添加顺序.
底层才有的是哈希表算法,查询效率极高.
判断两个对象是否相等的规则:
1):equals比较为true.
2):hashCode值相同.
要求:要求存在在哈希中的对象元素都得覆盖equals和hashCode方法.
LinkedHashSet:
HashSet的子类,底层也采用的是哈希表算法,但是也使用了链表算法来维持元素的先后添加顺序.
判断两个对象是否相等的规则和HashSet相同.
因为需要多使用一个链表俩记录元素的顺序,所以性能相对于HashSet较低.
一般少用, 如果要求一个集合既要保证元素不重复,也需要记录添加先后顺序,才选择使用LinkedHashSet.

TreeSet:不保证元素的先后添加顺序,但是会对集合中的元素做排序操作.
底层才有红黑树算法(树结构,比较擅长做范围查询).
TreeSet要么才有自然排序,要么定制排序.
自然排序: 要求在TreeSet集合中的对象必须实现java.lang.Comparable接口,并覆盖compareTo方法.
定制排序: 要求在构建TreeSet对象的时候,传入一个比较器对象(必须实现java.lang.Comparator接口).
在比较器中覆盖compare方法,并编写比较规则.
TreeSet判断元素对象重复的规则:
compareTo/compare方法是否返回0.如果返回0,则视为是同一个对象.
HashSet做等值查询效率高,TreeSet做范围查询效率高.
而我们更多的情况,都是做等值查询, 在数据库的索引中做范围查询较多,所以数结构主要用于做索引,用来提高查询效率.
12.Map
映射的数学解释:
设A、B是两个非空集合,如果存在一个法则f,使得对A中的每个元素a,按法则f,在B中有唯一确定的元素b与之对应,则称f为从A到B的映射,记作f:A→B。
映射关系(两个集合):A集合和B集合.
A集合中的每一个元素都可以在B集合中找到唯一的一个值与之对应.
严格上说,Map并不是集合,而是两个集合之间的映射关系(Map接口并没有继承于Collection接口),然而因为Map可以存储数据(每次存储都应该存储A集合中以一个元素(key),B集合中一个元素(value)),我们还是习惯把Map也称之为集合.
因为:Map接口并没有继承于Collection接口也没有继承于Iterable接口,所以不能直接对Map使用for-each操作.
在这里插入图片描述
Map的常用实现类:
HashMap: 采用哈希表算法, 此时Map中的key不会保证添加的先后顺序,key也不允许重复.
key判断重复的标准是: key1和key2是否equals为true,并且hashCode相等.
TreeMap: 采用红黑树算法,此时Map中的key会按照自然顺序或定制排序进行排序,key也不允许重复.
key判断重复的标准是: compareTo/compare的返回值是否为0.
LinkedHashMap: 采用链表和哈希表算法,此时Map中的key会保证先后添加的顺序,key不允许重复.
key判断重复的标准和HashMap中的key的标准相同.
Hashtable: 采用哈希表算法,是HashMap的前身(类似于Vector是ArrayList的前身).打死不用.
在Java的集合框架之前,表示映射关系就使用Hashtable.
所有的方法都使用synchronized修饰符,线程安全的,但是性能相对HashMap较低.
Properties: Hashtable的子类,此时要求key和value都是String类型.
用来加载资源文件(properties文件(IO再讲)).
一般的,我们定义Map,key都使用不可变的类(String),把key作为value的唯一名称.
HashMap和TreeMap以及LinkedHashMap都是线程不安全的,但是性能较高:
解决方案: Map m = Collections.synchronizedMap(Map对象);
Hashtable类实现线程安全的,但是性能较低.
哈希表算法:做等值查询最快.
数结构算法:做范围查询最快–>应用到索引上.

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

//需求 计算一个字符串中每一个字母出现的次数   (用键值对做)
public class MapDemo {
public static void main(String[] args) {
	String str = "iuhiubiuviuvivhvjhvjhggvvoitcicvbb";
	char[] arr = str.toCharArray(); //将字符串转为数组
	//Character-->key   ,Integer --> value
	//    存字符               出现次数
	Map<Character, Integer> map = new LinkedHashMap<>();
	for (char ch : arr) {
		if(map.containsKey(ch)){ //判断map中是否包含了该字符的key 
			Integer num = map.get(ch); //如果存在的话,取出该字符对应的value值 ++ ,在放回去
			map.put(ch, num+1);	 //将value值放回去
		}else{
			map.put(ch, 1); //表示当前map不包含该字符的key,  将ch字符保存为 key ,value值设置为1
		}
	}
	System.out.println(map);
}
}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_37282683/article/details/88749953