声明:学基础,在校学生,本文所有内容来自书本和视频,然后通过自己的理解和筛选编写而来,如有理解不到位的写得不到位的地方,欢迎评论指错!!!(仅做学习交流)
笔者:Fhvk
微信:WindowsC-
数组存储对象案例
public class Demo_Array {
public static void main(String[] agrs) {
Student[] arr = new Student[5];
arr[0] = new Student("李超武", 19);
arr[1] = new Student("陈清香", 20);
arr[2] = new Student("阳文杰", 21);
for(Student stu : arr) {
System.out.println(stu); //调用toString()方法
}
}
}
- 数组中存放的是地址
为什么要有集合&&集合和数组的区别&&集合体系图
- 为什么要有集合?因为数组的特点是长度固定,当添加元素超过了数组长度就要重新定义更大数组,JAVA内部给我们提拱了集合类,能存储任意对象,长度可变,随着元素增加而增加,随着元素减少而减少;
- 数组和集合的区别:
1、数组可以存储基本数据类型和引用数据类型,而集合只能存储引用数据类型(基本数据类型包装类)
2、数组长度是固定,集合长度是可变,随增就增,随减就减; - 数组和集合什么时候用?
1、如果元素个数固定的使用数组
2、如果元素个数不固定使用集合 - 体系图
Collection集合的基本功能
- 注意:add()方法在List和Set中都有返回boolean;在List中永远返回的是true,因为List中永远都存储得了重复的元素,Set中如果出现重复的元素就会返回false;从源码中可以看到;
集合遍历之数组遍历(知道就好)
- 这个知识点了解就好,后面会讲迭代器
- 就是通过toArray()方法返回该集合的数组形式,接收的是Object[];
public class Demo {
public static void main(String[] agrs) {
Collection<Integer> c = new ArrayList<Integer>();
c.add(1); //不是说只能存对象?而且泛型指定了Integer;这里应用到自动装箱;
c.add(2);
c.add(3);
Object[] arr = c.toArray();
for(Object obj : arr) {
System.out.println((Integer)obj);
}
}
}
集合中带All的功能
public class Demo_CollectionAll {
public static void main(String[] agrs) {
Collection c1 = new ArrayList();
c1.add("a");
c1.add("b");
c1.add("c");
c1.add("d");
Collection c2 = new ArrayList();
c2.add("a");
c2.add("b");
c2.add("c");
c2.add("d");
c1.addAll(c2); //将c2集合中的元素添加c1集合中
c1.add(c2); //将c2集合对象添加到c1集合中,以toString()
c1.removeall(c2); //删除交集,删除c1中和c2中元素一样的元素
c1.containsAll(c2); //判断c1中是否包含c2集合,包含返回true,不包含返回false
c1.retainAll(c2); //取交集,取出c1和c2中共同的元素;改变的是c1;返回boolean
}
}
集合迭代器(遍历)
- 集合是用来存储元素的,存储元素需要查看,那么就需要迭代(遍历)
- Iterator中三个抽象方法
1、hasNext();如果仍有元素可以迭代,则返回true;
2、next();返回迭代下一个元素;
3、remove();删除当前元素;
public class Demo_Iterator {
public static void main(String[] agrs) {
Collection c = new ArrayList();
c.add(new Student("李超武", 19, 60.1));
c.add(new Student("吴非凡", 20, 65.1));
Iterator it = c.iterator(); //获取迭代器
while(it.hasNext()) { //判断集合中是否有元素,有返回true,没有返回false;
Student stu = (Student)it.next();
System.out.println(stu.getName());
}
}
}
- 1、Iterator接口的功能是从前向后输出,属于单向的输出;
- 2、Iterator主要的是功能就是完成迭代输出操作;
- 3、在使用Iterator时候不要删除数据,换修改数据;
迭代器原理分析
- 为什么要把hasNext()、next()这样的方法向上抽取到接口?
- 迭代器是对集合进行遍历,而每一个集合内部的存储结构都是不同,所以每一个集合的存和取都是不一样的,那么就需要在每一个类中定义hasNext和next方法,这样做是可以的,但是会让整个集合体系过于臃肿,迭代器是将这样的方法向上抽取到接口,然后在每个类的内部,定义自己迭代方式,这样做的好处有二种:第一规定整个集合体系的遍历方式都是hasNext和next方法,第二代码由底层内部实现,使用者不用管怎么实现的,会用就可以了;
- 怎么实现的?进去去看源码ArrayList中iterator()方法发现返回的是Itr类实现了Iterator接口,重写了所有方法;代码实现自己理解;
Collection接口的子接口List
- List集合特有的功能
public class Demo_List {
public static void main(String[] agrs) {
List list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(2, 8); //指定位置添加元素
list.remove(1); //指定位置删除元素
list.get(1); //指定位置获取元素
list.set(1, 200);//指定位置获取元素
// 通过get()方法遍历
for(int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
- 在List集合中存储学生对象并遍历
public class Test {
public static void main(String[] agrs) {
List list = new ArrayList();
list.add(new Student("李超武", 19));
list.add(new Studnet("吴非凡", 20));
for(int i = 0; i < list.size(); i++) {
Student stu = (Student)list.get(i);
System.out.println("学生姓名为: " + stu.getName()):
}
}
}
并发修改异常的解决方法
// 在遍历的同时修改集合元素
public class Demo_Listiterator {
public static void main(String[] agrs) {
List list = new ArrayList();
list.add("a");
list.add("b");
list.add("c");
ListIterator ite = list.listIterator();
while(ite.hasNext()) {
if(ite.next().equals("c")) {
ite.add("d"); //通过ListIterator中的方法
}
}
System.out.println(list);
}
}
ListIterator
public class Demo_ListIterator {
public static void main(String[] agrs) {
List list = ArrayList();
list.add("a");
list.add("b");
list.add("c");
ListIterator lte = list.listIterator();
while(ite.hasNext()) {
System.out.print(ite.next());
}
//注意指针一定要指向最后一个元素,否则如果是从一开始遍历,那么一直都是false
while(ite.hasPrevious()) { //指针向前移,反着遍历
System.out.print(ite.pevious()); //返回上一个元素
}
}
}
- 注意:1、如果要想使用ListIterator则只能依靠List接口完成;
2、如果要进行由后向前输出,则只能先进行由前向后输出;
3、对于此接口中的增加及修改操作了解就好;
Vector
- 从jdk1.2此类改进实现了List接口
public class Demo_Vector {
public static void main(String[] agrs) {
Vector v = new Vector();
v.addElements("a");
v.addElements("b");
v.addElements("c");
Enumeration e = v.elements();
while(e.hasMoreElements()) {
System.out.println(e.nextElement());
}
}
}
- 1、在所有的输出操作中,以Iterator接口为最标准的输出操作,这一点一定要记住;
- 2、在部分旧的操作中Enumeration依然存在,限制在Vector集合中;
数据结构之数组和链表
- 简单说下
- 数组:查询快,增删慢;
- 链表:查询慢,增删快;
- 都是相对的,自己看下源码;
List的三个子类的特点
- ArrayList:底层是数组实现的;查询快,增删慢;线程不安全,效率高;
- Vector:底层是数组实现;查询快,增删慢;线程安全的;效率低;
- Vector相对ArrayList查询慢点(线程安全的)
- Vector相对是LinkedList增删慢(数据结构)
- LinkedList:底层是链表实现的;增删快,查询慢;线程不安全的效率高
- Vector和ArrayList的区别:Vector是线程安全的;效率底,ArrayList是线程不安全的,效率高;
- ArrayList和LinkedList的区别:ArrayList底层数组实现,查询修改快;LinkedList底层是链表实现的,增删快,查询和修改相对慢;
- 查询多用ArrayList,增删多用LinkedList,如果都多用ArrayList;
去除ArrayList中重复字符串的方式
public class Demo_ArrayList {
public static void main(String[] agrs) {
ArrayList list = new ArrayList();
list.add("a");
list.add("a");
list.add("b");
list.add("b");
ArrayList newList = getSingle(list);
}
//这下面有错误,观察next()方法
public static ArrayList getSingle(ArrayList list) {
ArrayList newList = new ArrayList();
Iterator ite = list.iterator();
while(ite.hasNext()) {
if(!newList.contains(ite.next)) {
newList.add(ite.next());
}
}
}
}
去除ArrayList中重复自己定义对象
- 如果是自定义对象,就必须重写equals()方法,因为contains()方法和remove()方法底层都是通过equals()方法进行判断的。上面字符串String底层重写equals()方法
//比如我要去除重复自定义对象,如果不重写equals()方法。就继承自Object中的equals()方法,那么方法比较的就是地址值;所以必须重写比较属性
public class Demo_Student {
public static void main(String[] agrs) {
ArrayList list = new ArrayList();
list.add(new Student("李超武", 19));
list.add(new Student("陈清香", 20));
list.add(new Student("李超武", 19));
list.add(new Student("陈清香", 20));
ArrayList newList = getSingle();
System.out.println(newList);
}
public static ArrayList getSingle(ArrayList list) {
ArrayList newList = new ArrayList();
Iterator ite = list.iterator();
while(ite.hasNext()) {
Object obj = ite.next();
if(!newList.contains(obj)) {
newList.add(obj);
}
}
}
}
LinkedList特有的功能
数据结构之栈和队列
- 简装说一下
- 栈:先进后出
- 队列: 先进先出
用LinkedList模拟栈数据结构的集合并测试
//单独写个类
public class Stack {
private LinkedList list = new LinkedList();
//进栈方法
public void in(Object obj) {
list.addLast(obj);
}
//出栈
public Object out() {
return list.removeLast();
}
//判断栈是否为空
public boolean isEmpty() {
return list.isEmpty();
}
}
//测试类
public class Test {
public static void main(String[] agrs) {
Stack s = new Stack();
s.in("a");
s.in("b");
s.in("c");
while(s.isEmpty()) {
System.out.print(s.out());
}
}
}
注意:实现队列同理,修改remove就可以;
jdk1.5新特性增强for的使用
- 简化数组和Collection集合的遍历,底层是Iterator(迭代器实现的);
//数组
in[] arr = {11, 22, 33, 44};
for(int i : arr) {
System.out.println(i);
}
//集合
ArrayList<String> list = new ArrayList<String>();
list.add("a");
list.add("b");
for(String string : list) {
System.out.println(string);
}
集合中三种删除的遍历
//普通for循环,删除后要减减,因为集合每删除一个元素都会自动向前移
for(int i = 0; i < list.size(); i++) {
if(list.get(i).equals("b")) { //集合中元素为b的删除
list.remove(i);
i--; //i一定要减减
}
}
//Iterator遍历,删除不能使用集合中remove中的删除方法,因为会出现并发修改异常;要使用Iterator中的remove方法
Iterator<String> ite = list.iterator();
while(ite.hasNext()) {
if("b".equals(ite.next)) {
ite.remove();
}
}
System.out.println(list);
//增强for循环,不能实现删除,肯定会出现并发修改异常,因底层是Iterator;而且又没有Iterator的引用,只能使用集合删除,异常是必须的;
for(String string : list) {
if(string.equals("b")) {
list.remove(string); //错
}
}
jdk1.5新特性静态导入
- 开发不用,知道就好
- 注意事项:方法必须是静态的,如果有多个同名的静态方法,容易不知道使用哪个,必须加前缀,由此义意不大,所以不用,但要能看得懂;
- Arrays里所有的类都是静态的
import static java.util.Arrays.sort; //导入排序方法,该源文件下所有的sort()方法不用加Arrays.
jdk1.5新特性可变参数(数组)
- 定义方法的时候,不知道定义多少个参数;
- 使用方法
public class Demo {
public static void main(String[] agrs) {
int[] arr = {1,2,3,4,5,6};
print(arr);
}
public static void print(int x, int ... arr) { //可变参数使用格式.其实就是一个数组
for(int i : arr) {
System.out.print(i + " ,");
}
}
}
输出:2,3,4,5,6; 为什么没有1?因为1给了x;没有打印
注意事项:多个参数时,可变参数必须放后面;
Arrays工具类中asList()方法
- asList()方法:数组转成集合;
- 注意事项:
1、数组转集合虽然不能添加和删除,但可以用集合思想操作数组,其它方法可以使用;
2、基本数据类型的数组如果通过asList()方法转成集合的话,会将整个数组当成一个对象转为集合;所以将数组转成集合,数组必须是引用数据类型
//数组转集合
public class Demo {
public static void main(String[] agrs) {
//引用数据类型
String[] arr = {"a", "b", "c"};
List<String> list = Arrays.asList(arr);
System.out.println(list);
}
}
//集合转数组
public class Demo {
public static void main(String[] agrs) {
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
//[0]给数组大小,但如果集合size的大小大于数组大小,那数组也会增大和集合一样大,如果数组大小大于集合,集合中的元素赋给数组,多于的大小全为null;
String[] arr = list.toArray(new String[0]);
for(String string : arr) {
System.out.println(string);
}
}
}
集合中套集合并遍历
public class Demo {
public static void main(String[] agrs) {
ArrayList<ArrayList<Person>> list = new ArrayList<>();
ArrayList<Person> stu1 = new ArrayList<>();
stu.add(new Person("S1", 19));
stu.add(new Person("S2", 20));
ArrayList<Person> su2 = new ArrayList<>();
stu.add(new Person("A1", 40));
stu.add(new Person("A2", 50));
list.add(stu1);
list.add(stu2);
for(ArrayList<Person> stu : list) {
for(Person p : stu) {
System.out.println(p);
}
}
}
}
HasSet
- Collection子接口Set的特点:不能存储重复元素,没有索引,存和取无序,没有啥特有方法和Collection中方法一样,不同的是add()方法可以返回false,当存储重复元素时返回false,不能用普通for循环遍历;1、没有get()方法;2、无序没索引,只能Iterator;
- 如下:
public static void main(String[] agrs) {
HashSet<String> has = new HashSet<>();
has.add("a");
has.add("b");
//当然也可以增强for,底层都是Iterator
Iterator<String> ite = has.iterator();
while(ite.hasNext()) {
System.out.println(ite.next());
}
}
存储自定义对象如何保证元素唯一性
- 自定义对象必须重写hashCode()方法和equals()方法,作用:去除重复对象;
- 我们使用Set集合都是要去除重复元素的,如果在存储的时候逐个equals()比较,效率太低,哈希算法提高了去重复的效率,降低了使用equals()方法次数;
- 当HashSet调用add()方法时,先调用对象的HashCode()方法得到一个哈希值,然后在集合中查找是否有哈希值相同的对象;
1、如果没有哈希值相同的对象就直接存入集合;
2、如果有哈希值相同的对象,就和哈希值一样的对象逐个进行equals()方法进行比较,结果fasle进行存入,true则不存 - hashCode():返回的是一个int类型的只希值,如果集合中有一样的,就会调用equals()运行比较,如果返回值int哈希值不一样,直接存入集合中;
- equals():属性相同返回true,不同返回false;
//自己重写
public boolean equals(Object obj) {
Person p = (Person)obj;
return p.name.equals(this.name) && p.age == this.age;
}
//尽量减少调用equals()
public int hashCode() {
final int NUM = 38;
return name.hashCode() * 38 + age;
}
//Eclipase生成的
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
public boolean equals(Object obj) {
if(this == obj) {
return true;
}
if(obj == null) {
return false;
}
if(getClass() != obj.getClass()) {
return false;
}
Person other = (Person)obj;
if(age != other.age) {
return flase;
}
if(name == null) {
if(other.name == null) {
return false;
}
}else if(!name.equals(other.name)) {
return false;
}
return true;
}
LinkedHashSet
- 是HashSet的子类,所以原理和HashSet一样,但因为是链表实现所以可以保证怎么存怎么取;
public class Demo {
public stataic void main(String[] agrs) {
LinkedHashSet<Integer> lhs = new LinkedHashSet<>();
lhs.add("a");
lhs.add("b");
lhs.add("b"):
System.out.println(lhs);
}
}
TreeSet
- 特点:保证元素唯一,并且是可以排序的;
- 如果保证自定义对象的唯一和排序:实现Comparato接口,并重写里面的compareTo()方法;方法返回正数放左边,返回负数放右边,返回0不存;
1 、返回0:集合中只有一个元素
2、返回-1:集合会倒序;
3、返回1:集合存怎怎取;
//Integer包装重写了compareTo()方法,所以具有自动排序功能
public class Demo {
public static void main(String[] agrs) {
TreeSet<Integer> ts = new TreeSet<>();
ts.add(23);
ts.add(21);
ts.add(25);
System.out.println(ts); //输出21 23 25;
}
}
TreeSet自定义对象如何进行排序
- 重写compareTo()方法:下面用一个学生类做比较
TreeSet使用比较器实现排序(Comparator)
- TreeSet有参构造中可以传入一个比较器多象,比较器是个接口,先实现它,并重写compare()方法,原理和compareTo()方法一样;该接有两个方法一个compare()一个equals(),equals()方法不用你重写会默认继承Object中的equals();
class CompareByLen imelements Comparator<Student> {
//按照姓名长度,进行排序,如果长度一样,名字不一样,按照姓名排序;如果名字长度和姓名都一样就按年龄排序
public int compare(Student o1, Student 02) {
int num = o1.getName().length() - o2.getName().length();
return num == 0 ? o1.getName().equals(o2.getName()) ? o1.getAge() - o2.getAge() : o1.getName().compareTo(o2.getName()) : num;
}
}
TreeSet总结
-
特点:TreeSet是可以用来排序的集合,可以指定一个顺序,对象存入之后会按指定的顺序排列;有两种方法实现排序
-
自然排序:1、对象必须实现Comparable接口,然后重写compareTo()方法
2、TreeSet类的add()方法中会将存入的对象提为Comparable
3、然后调用对象的compareTo()和集合中的对象进行比较
4、根据compareTo()方法返回的结果进行存储; 1右,-1左,0不存; -
比较器排序:1、创建一个类实现Comparator接口,重写compare()方法,然后通过匿名内部类将Comparator传给TreeSet有参构造,该集合就会按照比较器排序;
2、add()方法会自动调用compare()方法;3、compare()方法中有两个参数,第一个对象是自己,第二个对象是集合中的对象; -
两种方法的区别:如果按照自然排序,就必须实现Comparable重写compareTo()方法,否则什么都不写,就会报错(ClassCastException);但是如果两次方法都实现了,优先比较器;
SortedSet接口
- TreeSet是可以排序的操作类,TreeSet实际上也是SortedSet接口的子类,些接口所有的类都是可以排序的;
- SortedSet中定义的方法
- 注意:只要是以Sorted开头的接口,基本上都是表示可以排序的接口;所有实现类都可以排序都是通过Comparable实现的;
Map集合
- 一个键对应一个值
- map集合和Collection接口的区别
map是双列集合,它是双列集合的根接口;
Collection是单列集合,它时单列集合的根接口;
map集合的数据结构针对键有效,跟值无关;Collection集合的数据结构是针对元素有效;
单列集合底层依赖的map集合
Map集合的功能概述
- 添加功能
public class Demo {
public static void main(String[] agrs) {
Map<String, Integer> m = new HashMap<>();
m.put("张三", 23); //返回null
m.put("李四", 24); //返回 null
m.put("王五", 25); //返回null
m.put("张三", 26); //返回23,键是唯一的,它就会把原来的值覆盖掉,返回被覆盖的值;
System.out.println(m);
}
}
-
删除功能
clear();删除所有的键和值元素
remove(Object key);通过键删除元素,并把值返回; -
判断功能
containsKey(Object key);判断是否包含这个键
containsValue(Object value);判断是否包含这个值
isEmpty()判断是否为空; -
获取功能
Set<Map.Entry<k, v>> entrySet();拿到所有的键值对象;
V get(Objetc key);根据键获取值;
Set keySet();获取该集合所有的键;
Collection values();获取集中所有的值; -
长度功能
int size();返回集合中元素个数;
Map集合的遍历(通过键找值)
- API可以看出双集合没有iterator方法,那么双列如何迭代呢?
//迭代器
public class Dmeo {
public static void main(String[] agrs) {
Map<String, Integer> map = new HashMap<>();
map.put("张三", 23);
map.put("李四", 24);
//获取所有键,返回一个Set集合
Set<String> keyset= map.keySet();
Iterator<String> ite = keyset.iterator();
while(ite.hasNext()) {
String key = ite.next;
Integer value = map.get(key);
System.out.println("key : " + key + ", value : " + value );
}
}
}
//增强for循环
public class Demo {
public static void main(String[] agrs) {
Map<String, Integer> map = new HashMap<>();
map.put("张三" , 23);
map.put("李四", 24);
for(String string : map.keySet()) {
System.out.println("key : " + string + " , value : " + map.get(string));
}
}
}
Map集合的遍历(通过键值对象找键和值)
- 通过entrySet()方法获取集合中所有的键值对象;这个键值对象是Map接口中的内部接口,Map.Entry<Key, Value>;该接口中有getKey()和getValue()可以获取键和值;
//迭代器遍历
public class Demo {
public static void main(String[] agrs) {
Map<String, Integer> map = new HashMap<>();
map.put("李超武", 10);
map.put("张三", 20);
//获取map集合中所有的键值对对象,返回给Set集合;
Set<Map.Entry<String, Integer>> entry = map.entrySet();
//获取迭代器
Iterator<Map.Entry<String, Integer>> ite = entry.iterator();
while(ite.hasNext()) {
Map.Entry<String, Integer> en = ite.next();
System.out.println(en.getKey() + " .." + en.getValue());
}
}
}
public class Demo {
public static void main(String[] agrs) {
Map<String, Integer> map = new HashMap<>();
map.put("李超武", 19);
map.put("张三", 20);
for(Map.Entry<String, Integer> en : map.entrySet() )
}
}
HashMap存储自定义对象在键位置,怎么保证键唯一
- 因为是哈希算法,重写hashCode()方法和equals(),哈希值尽量不一样就直接存,一样就调用equals()方法进行比较;写法和HashSet一样;
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + (this.name == null ? 0 : this.name.hashCode());
return reult;
}
public boolean equals(Object obj) {
if(this == obj) {
return true;
}
if(obj == null) {
return false;
}
if(this.getClass != obj.getClass()) {
return false;
}
Student s = (Student)obj;
if(s.age != this.age) {
return false;
}
if(name == null) {
return false;
}else if(!name.equals(s.name)) {
return false;
}
return true;
}
LinekedHashMap
- 底层也是链表结构,所以保证怎么存就怎么取;使用方法差不多;
public class Demo {
public static void main(String[] agrs) {
LinkedHashMap<String, Integer> lhm = new LinekedHashMap<>();
lhm.put("李超武", 19);
lhm.put("陈清香", 20);
for(String key : lhm.keySet()) {
System.out.println("键 : " + key + " , 值 : " + lhm.get(key));
}
}
}
TreeMap
- 基本和TreeSet一样,也是要么实现Comparable,要么在TreeMap构造中传入Comparator子类对象(比较器),分别重写compareTo()和compare();
public class Demo {
public static void main(String[] agrs) {
TreeMap<Student, Integer> tm = new TreeMap<>(new Comparator<Student>() {
public int compare(Student s1, Student s2) {
int num = s1.getName().compareTo(s2.getName());
return num == 0 ? s1.getAge() - s2.getAge() : num;
}
});
tm.put(new Student("a", 10), 100);
tm.put(new Student("c", 12), 100);
tm.put(new Student("b", 11), 101);
for(Student key : tm.keySet()) {
System.out.println("键:" + key + " = " + tm.get(key));
}
}
}
Map集合中嵌套Map集合
public class Demo {
public static void main(String[] agrs) {
HashMap<Student, String> hm1 = new HashMap<>();
hm1.put(new Student("张三",23), "湖南");
hm1.put(new Student("李四", 24), "湖南");
HashMap<Stuednt, String> hm2 = new HashMap<>();
hm2.put(new Student("王五", 25), "上海");
hm2.put(new Student("赵六", 26), "北京");
HashMap<HashMap<Student, String>, String> hm = new HashMap<>();
hm.put(hm1, "一班");
hm.put(hm2, "二班");
//遍历
for(HashMap<Student, String> h : hm.keySet()) {
String value = hm.get(h);
for(Student stu : h.keySet()) {
System.out.println(stu + "=" + h.get(stu) + "=" + value);
}
}
}
}
HashMap与Hashtable的区别
- 共同点:都是双列集合,底层都是哈希算法;
- 不同点:HashMap是线程不安全的,效率高的,JDK1.2;Hashtable是线程安全的,效率低,是JDK1.0的;HashMap可以键值可以是null,Hashtable不可以是null;
集合工具类(Collections)
模拟斗地主发牌、洗牌、并排序
public class Demo {
public static void main(String[] agrs) {
String[] name = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"};
String[] color = {"黑桃","红桃","梅花","方片"};
HashMap<Integer, String> hm = new HashMap<>();
ArrayList<Integer> list = new ArrayList<>();
int index = 0;
for(String n : name) {
for(String c : color) {
hm.put(index, c.concat(n));
list.add(index);
index++;
}
}
hm.put(index, "小王");
list.add(index);
index++;
hm.put(index, "大王");
//洗牌
Collections.shuffle(list);
//发牌并用排序
TreeSet<Integer> li1 = new TreeSet<>();
TreeSet<Integer> li2 = new TreeSet<>();
TreeSet<Integer> li3 = new TreeSet<>();
TreeSet<Integer> dipai = new TreeSet<>();
for(int i = 0; i < list.size(); i++) {
if(i >= list.size() - 3) {
dipai.add(list.get(i));
}else if(i % 3 == 0) {
li1.add(list.get(i));
}else if(i % 3 == 1) {
li2.add(list.get(i));
}else {
li3.add(list.get(i));
}
}
//显示
print(hm, li1, "张三");
print(hm, li2, "李四");
print(hm, li3, "王五");
print(hm, dipai, "底牌");
}
public static void print(HashMap<Integer,String> hm, TreeSet<Integer> ts, String name) {
System.out.print("玩家" + name + ": ");
for(Integer ite : ts) {
System.out.print(hm.get(ite) + ", ");
}
System.out.println();
}
}
WeakHashMap(弱引用类)
- 如果假设一个Map中某些内容长时间不使用的话,按照之前的做法是不会删除的,如果希望可以自动删除,可以使用这个类,当里面的某些内容不使用时,可以自动删除
public class Demo_WeakHashMap {
public static void main(String[] agrs) {
WeakHashMap<String, String> whm = new WeakHashMap<>();
whm.put(new String("ss"), new String("sada"));
whm.put(new String("ddd"), new String("dsf"));
whm.put("xc", "sad");
System.gc(); //强制垃圾回收
System.out.println(whm);
}
}
IdentityHashMap
- 键可以重复,比较的是地址值;
对象引用强度说明
- 强引用:当内存不足时,JVM宁可出现OutOfMemeryError错误而使程序停止,也不会回收此对象来释放空间;
- 软引用:当内存不足时,会回收这些对象的内存,用来实现内存敏感的高速缓存;
- 弱引用:无论内存是否紧张,被垃圾回收器发现立即回收;
- 虚引用:和没有任何引用一样;
SortedMap
- 作用、方法和SortedSet一样;
public class Demo_Sorted {
public static void main(String[] agrs) {
SortedMap<String, String> sm = new SortedMap<>();
sm.put("a", "123");
sm.put("b", "456");
sm.put("c", "789");
System.out.println("第一个Key : " + sm.firstKey());
System.out.println("对应的值 : " + sm.get(sm.firstKey()));
System.out.println("最后一个Key : " + sm.lastKey());
System.out.println("对应的值 : " + sm.get(sm.firstKey()));
System.out.println("返回指定元素前的集合:");
for(Map.Entry<String, String> me : sm.headMap("c").entrySet()) {
System.out.println(me.getKey() + "-->" + me.getValue());
}
System.out.println("返回指定元素后的后集合:");
for(Map.Entry<String,String> me : sm.tailMap("a").entrySet()) {
System.out.println(me.getKey() + "-->" + me.getValue());
}
System.out.println("截取指定元素间的集合 : ");
for(Map.Entry<String, String> en : sm.subMap("a", "c").entrySet()) {
System.out.println(en.getKey() + "-->" + en.getValue());
}
}
}
- 方法包含头不包含尾
Stack类
- 栈是数据结构中比较常见的一种形式,栈是使用典型的先进后出的操作方式完成的;Java中Stack就是栈类;它不是Vector的子类;
- empty();判断是否空;
- peek();查看栈顶,但不删除;
- pop();出栈,同时删除;
- push(E obj);入栈
- search(Object obj);在栈中查找;
public class Demo_Stack {
public static void main(String[] agrs) {
Stack<String> stack = new Stack<>();
//入栈
stack.push("a");
stack.push("b");
stack.push("c");
//出栈
stack.pop();
stack.pop();
stack.pop();
}
}
- 了解Stack的使用,了解入栈及出栈操作,以及先进后出的功能;
Properties
-
属性是在程序中经常出现的一种形式;在类集中专门提拱了一个Properties类,以完成属性的操作;是Hashtable的子类,则也是Map的子类,可以使用Map的全部操作,一般情况下属性类是单独使用的;
-
写出到xml
-
写出到普通文件
-
同样还有输入操作分别使用方法load(InputStream)和loadFromXml(InputStream)
一对多的关系
- 以一个学校有多个学生,一个学生只有一个学校的关系案例
//定义学生类
public class Studnet {
private String name;
private int age;
private School scool; //每个学生只有一个学校
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public void setSchool(School school) {
this.school = school;
}
public School getSchool() {
return this.school;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
public String toString() {
return "姓名 : " + this.name + ", 年龄 : " + this.age;
}
}
//定义学校类
public class School {
private String name;
private List<Student> list;
public School() {
list = new ArrayList<Student>();
}
public School(String name) {
this();
this.name = name;
}
public List<Student> getList() {
return list;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public String toString() {
return "学校: " + this.name;
}
}
//测试类
public class Test {
public static void main(String[] agrs) {
//创建一个学校校
School school = new School("湖南交通工程学院");
//创建学生
Student s1 = new Student("李超武", 19);
Student s2 = new Student("吴非凡", 19);
Student s3 = new Student("谢仔晟", 18);
//学生添加到该学校
school.getList().add(s1);
school.getList().add(s2);
school.getList().add(s3);
//给学生设置学校
s1.setSchool(schoool);
s2.setSchool(schoool);
s3.setSchool(schoool);
//迭代输出
Iterator<Student> iter = school.getList().iterator();
while(iter.hasNext()) {
Student stu = iter.next();
//学生信息
System.out.print("-->" + stu);
//学生所在学校
System.out.println("-->" + stu.getSchool().getName());
}
}
}
- 总结:明白类类集的关系,那么这种关系将称为日后标准程序的开发基础;1、类的设计;2、类集关系;
多对多的关系
- 一个学生可以选多门课程,一门课程可以有多名学生参加,那么这就是多对多关系
//定义学生类
public class Student {
private String name;
private int age;
private List<Course> list;
public Student() {
list = new ArrayList<>();
}
public Student(String name. int age) {
this();
this.name = name;
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public int getAge() {
return this.age;
}
public String getName() {
return this.name;
}
public List<Course> getAllCourse() {
return list;
}
public String toString() {
return "姓名 : " + this.name + ", 年龄 : " + this.age;
}
}
//定义课程类
public class Course {
private String name; //课程名称
private int credit; //该课程学分
private list<Student> list; //该课程参加的学生
public Course() {
list = new ArrayList<>();
}
public Course(String name, int credit) {
this();
this.name = name;
this.credit = credit;
}
public void setName(String name) {
this.name = name;
}
public void setCredit(int credit) {
this.credit = credit;
}
public int getCredit() {
return this.credit;
}
public String getName() {
return this.name;
}
public List<Student> getAllStudent() {
return list;
}
public String toString() {
return "课程名称 : " + this.name + ", 课程学分 : " + this.credit;
}
}
//测试类
public class Test {
public static void main(String[] sgrs) {
Course c1 = new Course("C语言", 3); //定义C语言课程
Course c2 = new Course("JAVA", 5); //定义JAVA课程
//定义三个学生
Student s1 = new Student("张三" ,19);
Student s2 = new Student("李四", 20);
Student s3 = new Student("王五", 21);
//C语言有三名同学参加
c1.getAllStudent().add(s1);
c1.getAllStudent().add(s2);
c1.getAllStudent().addf(s3);
//JAVA有两名同学参加
c2.getAllStudent().add(s2);
c2.getAllStudent().add(s3);
//输出C语言的信息,观察有多少同学参加
System.out.println(c1);
Iterator<Student> iter1 = c1.getAllStudent().iterator();
while(iter1.hasNext()) {
System.out.println("\t--" + iter1.next());
}
//输出张三参加了哪些课程
System.out.println(s1);
Iterator<Course> iter2 = s1.getAllCourse().iterator();
while(iter2.hasNext()) {
System.out.println("\t--" + iter.next());
}
}
}
- 此处多对多关系只是在这种单独类,这样的类可以表示成实体类,只是在这上面设置的关系;
总结
(单列集合)
- Collection接口中有List和Set两个接口
- List:存取有序,有索引,可以存重复元素;
- Set:存取无序,无索引,不可以存重复元素;
- List有三个实现类:ArrarList、LinkedList、Vector\
- ArrayList:底层是数组实现的,是线程不安全的;查找修改快,增删慢;
- LinkedList:底层是链表实现的,是线程不安全的;增删快,查找修改慢;
- Vector:底层也是数组,线程安全,增删查改都慢;
- 什么情况使用这些类?
1、如果删除和增加多,使用LinkedList
2、如果修改和查找多,使用ArrayList
3、如果都多使用ArrayList - Set有三个实现类:HashSet、LinkedHashSet、TreeSet
- HashSet:底层是哈希算法实现的,不能保证怎么存怎么取
- LinkedHashSet:底层是链表实现,也可以保证元素唯一(哈希算法),还保证了存取有序(链表)
- TreeSet:底层是二叉树实现,可以排序。自定义对象但要实现Comparator\Comparable接口
- 什么情况使用这些类?
1、开发不需要对元素进行排序;所以直接用HashSet,效率也高点
2、TreeSet在面试用得比较多吧
(双列集合)
Map接口中有三个实现类:HashMap、LinkedHashMap、TreeMap
HashMap:底层也是哈希算法,所以特点和HashSet一样,但是算法只针对键有效;
LinkedHashMap:底层是链表实现,所以特点和LinkedHashSet一样,但数据结构只针对键有效,能保证怎么存怎么取;
TreeMap:底层是二叉树实现,所以特点和TreeSet一样,但是数据结构也是只针对键有效; - 什么时候使用这些类:
1、开发中用不需要对键排序,所以直接HashMap,效率高;
2、TreeMap在面试中用得多;
面试题
- Collection和Collections的区别?
答:Collection是单列集合顶层接口,Collections是个类是个集合工具类,该类有中有很多操作集合的静态方法;
- 说出ArrayList、Vector、LinkedList存储性能和特性
答:ArrayList\Vector都是使用数组方式存储数据,所以对于播入数据和删除数据比较慢,因为涉及数组元素移动等内存操作,数组查找起来比较快,Vector使用synchronized方法(也就是同步,也就是线程安全),性能上比ArrayList差点;LinkedList使用双向链表实现存储,所以插入数据和删除数据比较快,而查找起来比较麻烦,链表或者从头或从尾开始遍历,所以比较慢;
- HashMap和Hashtable的区别?
答:HashMap是线程不安全的,Hashtable是线程安全的;HashMap键\值允许存储null,Hashtable不允许;方法上HashMap替换了Hashtable中的contains(),变为containsValue()和containsKey();