Day17-Java
文章目录
1、类集
1.1 认识类集
如果现在要想保存多个对象,肯定使用对象数组完成,但是对象数组本身有一个最大问题在于确定了数据的长度,所以后来使用了链表完成了动态对象数组的开发,可是链表的开发难度实在是很大,而且如果一个链表要想真正的去使用,只依靠之前多编写的还不够,还需要进行一些代码的调优。
而在JDK1.2之后正式引入了类集的概念,类集是一种动态的对象数组,属于各个数据结构的实现类,在整个类集之中主要的组成是一些核心的操作接口:Collection,List,Set,Map,Iterator,Enumeration。
1.2 Collection集合接口
Collection是单个集合保存最大的父接口。而在Collection接口的定义如下:
public interface Collection<E> extends Iterable<E>
从JDK1.5之后Collection接口上追加有泛型应用,这样的直接好处就是避免了ClassCastException异常,里面的所有的数据的保存类型应该是相同的。对于此类的常用方法有如下几个:
方法名称 | 类型 | 描述 |
public boolean add(E e) | 普通 | 向集合中添加数据 |
boolean addAll(Collection<? extends E> c) | 普通 | 向集合添加一组数据 |
public void clear() | 普通 | 清空集合数据 |
public boolean contains(Object o) | 普通 | 查询数据是否存在 |
public Boolean isEmpty() | 普通 | 判断集合是否有元素 |
public Iterator iterator() | 普通 | 取得Iterator接口对象,用于输出 |
public boolean remove(Object o) | 普通 | 删除数据,需要equals()方法 |
public int size() | 普通 | 取得集合的长度 |
public Object[] toArray() | 普通 | 将集合数据返回 |
在开发之中,add()和iterator()方法使用率极高,其他的方法几乎使用不到。接口只是一个存储数据的标准,而并不能区分存储类型,例如:如果要存放数据可能需要区分重复与不重复。所以在实际的开发之中,往往会去考虑使用Collection接口的子接口:List(允许重复)、Set(不允许重复)。
1.3 List接口简介
List是Collection的一个最常用的子接口,并且允许重复的子接口。
方法名称 | 类型 | 描述 |
public E get(int index) | 普通 | 取得指定索引位置上的数据 |
public E set(int index,E element) | 普通 | 修改指定索引上的数据 |
public ListIterator listIterator() | 普通 | 为ListIterator接口实例化 |
List子接口与Collection接口相比最大的特点在于其有一个get()方法,可以根据索引取得内容。但是List本身还属于我们的一个接口而如果取得接口的实例化对象就必须有子类,在List接口下有三个常用子类:ArrayList、Vector、LinkedList。
最终的操作应该还是以接口为主,那么既然要以接口为主,所以所有的方法只参考接口的定义即可。
1.3.1 ArrayList子类
ArrayList是一个针对于List接口的数组操作实现。
List基本操作
package com.day17.demo; import java.util.ArrayList; import java.util.List; public class ArrayListDemo { public static void main(String[] args) { List<String> all = new ArrayList<>();//此时集合里面只适合保存String类型数据 System.out.println(all.size() + " " + all.isEmpty()); all.add("Hello"); all.add("Hello"); //重复数据 all.add("world~!"); all.add("zsr~"); System.out.println(all.contains("zsr~")); System.out.println(all.contains("zsr")); System.out.println(all); } }
通过我们的代码我们可以证实List允许保存重复数据。
List中存在get()方法,可以利用get()方法结合索引取得数据。
List的get()方法
package com.day17.demo; import java.util.ArrayList; import java.util.List; public class ArrayListDemo { public static void main(String[] args) { List<String> all = new ArrayList<>();//此时集合里面只适合保存String类型数据 all.add("Hello"); all.add("Hello"); //重复数据 all.add("world~!"); all.add("zsr~"); for (int i = 0; i < all.size(); i++) { System.out.println(all.get(i)); } } }
但是千万记住,get()方法是List子接口的,如果现在使用不是List而是Collection,对于此时的数据取出,只能够将集合变为对象数组的操作了。
(开发一般不使用)Collection进行输出处理并取出数据
package com.day17.demo; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; public class CollectionDemo { public static void main(String[] args) { Collection<String> all = new ArrayList<>();//此时集合里面只适合保存String类型数据 all.add("Hello"); all.add("Hello"); //重复数据 all.add("world~!"); all.add("zsr~"); //操作以Object形式返回,那么就有可能需要向下转型,有可能造成ClassCastException的安全隐患 Object result [] = all.toArray();//变为Object对象数组 System.out.println(Arrays.toString(result)); } }
集合与简单Java类
在实际的开发之中,集合里面保存最多的数据类型,就是简答java类。
向集合保存简单java类
package com.day17.demo; import java.util.ArrayList; import java.util.List; class Person{ private String name; private Integer age; public Person(String name, Integer age) { super(); this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public boolean equals(Object obj) { // TODO Auto-generated method stub if(this == obj){ return true; } if (obj == null){ return false; } if(!(obj instanceof Person)){ return false; } Person per = (Person) obj;//这个对象自己操作可以向下转型 return this.name.equals(per.name) && this.age.equals(per.age); } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } } public class ListDemo { public static void main(String[] args) { List<Person> all = new ArrayList<>(); all.add(new Person("张三",10)); all.add(new Person("李四",21)); all.add(new Person("王五",19)); //对于remove()、contains()方法必须类中有equals()的支持 all.remove(new Person("李四",21)); System.out.println(all.contains(new Person("李四",21))); for (int i = 0; i < all.size(); i++) { System.out.println(all.get(i)); } } }
该List集合如果使用remove()、contains()方法必须有equals()方法的支持。简单的java类里面的使用是很少使用。
1.3.2 Vector子类
Vector是旧的子类,这个类是从JDK1.0退出,ArrayList实在JDK1.2推出。
package com.day17.demo; import java.util.ArrayList; import java.util.List; import java.util.Vector; public class ArrayListDemo { public static void main(String[] args) { List<String> all = new Vector<>();//此时集合里面只适合保存String类型数据 all.add("Hello"); all.add("Hello"); //重复数据 all.add("world~!"); all.add("zsr~"); for (int i = 0; i < all.size(); i++) { System.out.println(all.get(i)); } } }
面试题:请解释ArrayList与Vector区别?
区别 | ArrayList | Vector |
历史时间 | JDK1.2 | JDK1.0 |
处理形式 | 异步处理,形式更高 | 同步处理,性能降低 |
数据安全 | 非线程安全 | 线程安全 |
输出形式 | Iterator、ListIterator、foreach | Iterator、ListIterator、foreach、Enumeration |
1.3.3LinkedList子类
在List接口里面还有一个LinkedList子类,如果向我们的父接口转型的话,使用的形式和之前没有任何的区别。
package com.day17.demo; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; public class ArrayListDemo { public static void main(String[] args) { List<String> all = new LinkedList<>();//此时集合里面只适合保存String类型数据 all.add("Hello"); all.add("Hello"); //重复数据 all.add("world~!"); all.add("zsr~"); for (int i = 0; i < all.size(); i++) { System.out.println(all.get(i)); } } }
面试题:请解释ArrayList与LinkedList区别?
区别 | ArrayList | LinkedList |
构造方法 | public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } |
public LinkedList() { } |
开辟长度 | 开辟定长的大小 | 动态开辟 |
时间复杂度 | 时间复杂度为1 | 时间复杂度为n |
1.4 Set接口简介
Set接口与List接口最大的区别就是内容是不允许重复的,通知Set和List最大的区别还有一个就是
set接口没有对Collection接口进行扩充,而List对Collection接口进行了扩充。由于jdk1.8的原因,所以在collection接口也提供有一些default方法,而这写方法并没有在Set接口里面出现。也就是说set接口里面不可能使用get()方法进行处理,而在Set子接口里面有我们常用的子类HashSet、TreeSet。
1.4.1 散列存放的子类:HashSet
Hash(哈希)属于一种算法,这种算法的核心意义指的是找空保存算法,所以只要一看见Hash就是说没有顺序的保存。
观察Set接口的使用
package com.day17.demo; import java.util.HashSet; import java.util.Set; public class HashSetDemo { public static void main(String[] args) { Set<String> all = new HashSet<>(); all.add("Hello"); all.add("zsr"); all.add("zsr"); all.add("Abc"); System.out.println(all); } }
保存数据在输出之后发现,重复的数据没有了,并且其本身的保存也是没有任何顺序的。
1.4.2 排序存放的子类:TreeSet
如果现在希望Set集合之中保存的数据有顺序,那么就通过TreeSet进行Set接口的实例化。
使用TreeSet实例化接口
package com.day17.demo; import java.util.HashSet; import java.util.Set; import java.util.TreeSet; public class HashSetDemo { public static void main(String[] args) { Set<String> all = new TreeSet<>(); all.add("C"); all.add("C"); all.add("A"); all.add("B"); all.add("D"); System.out.println(all); } }
现在发现所有的保存的数据没有重复且有顺序。TreeSet使用的是一个升序排列的模式完成的。
1.4.3 关于TreeSet排序的说明
通过之前的程序可以发现,使用TreeSet实例化Set接口之中,所有保存的数据都是有序的,那么在这种情况下,那么如果说使用一个自定义的类呢?
那么这个时候如果这个类的对象要进行排序的话,则这个类必须实现Comparable接口,设置比较规则,但是在这种情况下有一点必须注意:一旦使用了Comparable的话,类之中所有的属性都必须写进排序规则。
自定义排序
package com.day17.demo; import java.util.Set; import java.util.TreeSet; class Personn implements Comparable<Personn>{ private String name; private Integer age; public Personn(String name, Integer age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public String toString() { return "person [name=" + this.name + ", age=" + this.age + "]\n"; } @Override public int compareTo(Personn o) { // TODO Auto-generated method stub if(this.age > o.age){ return 1; }else if (this.age < o.age){ return 0; }else{ return this.name.compareTo(o.name); } } } public class TreeSetDemo { public static void main(String[] args) { // TODO 自动生成的方法存根 Set<Personn> all=new TreeSet<Personn>(); all.add(new Personn("张三",20)); all.add(new Personn("张三",20)); all.add(new Personn("李四",20)); all.add(new Personn("王五",30)); all.add(new Personn("赵六",40)); System.out.println(all); } }
因为在实际开发之中TreeSet的使用实在是过于麻烦了,在项目开发的简单java类是根据数据库表的设计而来的,如果一张数据表的字段过多,你这个类得写死。
TreeSet子类依靠Compara()方法的返回值是否为0来判断是否为重复元素.
1.4.4 关于重复元素的说明
在使用TreeSet子类进行数据保存的时候,重复元素的判断依靠的是Comparable接口完成的。这并不是全部Set接口判断重复元素的方式,因为如果使用的是HashSet子类,由于其Comparable没有任何关系,所以他判断重复重复元素的主要依靠的是两个方法:
- hash码:public int hashCode();
- 对象比较:public Boolean equals(Object obj)
在进行对象比较的过程之中,首先会使用hashCode()与保存在集合之中的对象的hashCode()进行比较,如果代码相同,则再使用equals()方法进行内容的比较,如果全部相同,则为相同元素。
package com.day17.demo; import java.util.Set; import java.util.TreeSet; class Personn implements Comparable<Personn>{ private String name; private Integer age; public Personn(String name, Integer age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((age == null) ? 0 : age.hashCode()); 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; Personn other = (Personn) obj; if (age == null) { if (other.age != null) return false; } else if (!age.equals(other.age)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } @Override public String toString() { return "person [name=" + this.name + ", age=" + this.age + "]\n"; } @Override public int compareTo(Personn o) { // TODO Auto-generated method stub if(this.age > o.age){ return 1; }else if (this.age < o.age){ return 0; }else{ return this.name.compareTo(o.name); } } } public class TreeSetDemo { public static void main(String[] args) { // TODO 自动生成的方法存根 Set<Personn> all=new TreeSet<Personn>(); all.add(new Personn("张三",20)); all.add(new Personn("张三",20)); all.add(new Personn("李四",20)); all.add(new Personn("王五",30)); all.add(new Personn("赵六",40)); System.out.println(all); } }
如果要想标识出对象的唯一性,一定需要hashCode()、equals()共同作用。
面试题:如果两个hashCode()相同、equals()不同结果是什么?不能消除
面试题:如果两个hashCode()不相同、equals()相同结果是什么?不能消除
对象判断必须两个都要实现。
1.5 集合的输出操作
在之前所介绍的都属于单值集合的基本操作,可是对于集合有一个最重要的问题就是如何进行集合内容的输出操作,而这个问题在Java的类集框架之中给出了四种输出方式:Iterator,ListIterator,Enumeration,foreach。
1.5.1 迭代输出:Iterator
public boolean hasNext() | 判断是否有下一个元素 |
public E next() | 取得当前元素 |
public default void remove() | 删除元素 |
标准Iterator
package com.day17.demo; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; public class ArrayListDemo { public static void main(String[] args) { List<String> all = new ArrayList<>();//此时集合里面只适合保存String类型数据 all.add("Hello"); all.add("Hello"); //重复数据 all.add("world~!"); all.add("zsr~"); Iterator<String> iter = all.iterator();//实例化Iterator while(iter.hasNext()){ String str = iter.next(); System.out.println(str); } } }
对于Iterator接口中提供的remove()方法主要解决的就是集合内元素删除的问题
remove操作
package com.day17.demo; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedList; import java.util.List; public class ArrayListDemo { public static void main(String[] args) { List<String> all = new ArrayList<>();//此时集合里面只适合保存String类型数据 all.add("Hello"); all.add("Hello"); //重复数据 all.add("a"); all.add("world~!"); Iterator<String> iter = all.iterator();//实例化Iterator while(iter.hasNext()){ String str = iter.next(); if("a".equals(str)){ all.remove("a");//如果使用此操作后面的中断执行了 //iter.remove();//如果不中断后续输出,则执行 continue ; } System.out.println(str); } } }
以后只要是见到了集合的输出操作,永远都是用Iterator接口完成。
1.5.2 双向迭代输出:ListIterator
Iterator可以完成的是由前向后的单项输出操作,如果现在希望可以完成由前向后,由后向前的输出的话,那么就可以利用ListIterator接口完成,此接口是Iterator的子接口,在ListIterator接口主要使用一下两个扩充方法:
判断是否有前一个元素:public Boolean hasPrevious();
取出前一个元素:public E previous()。
但是如果要想取得ListIterator接口的实例化对象,Collection没有这样的方法支持,这个方法在List接口之中存在:
Public ListIterator listIterator()
执行双向迭代
package com.day17.demo; import java.util.ArrayList; import java.util.List; import java.util.ListIterator; public class ListIteratorDemo { public static void main(String[] args) { // TODO 自动生成的方法存根 List<String> all=new ArrayList<String>(); all.add("hello"); all.add("hello"); all.add("world"); ListIterator<String> ite=all.listIterator(); System.out.println("由前向后"); while(ite.hasNext()){ String str=ite.next(); System.out.print(str + "、"); } System.out.println(); System.out.println("由后向前"); while(ite.hasPrevious()){ String str=ite.previous(); System.out.print(str + "、"); } } }
但是对于由后向前的操作,在进行之前一定发生由前向后的输出。由于此输出接口只有List可以使用,所以在开发之中几乎不会出现。
1.5.3 废弃的接口:Enumeration
Enumeration是一个最早的输出接口,最早成为枚举输出,在JDK1.0的时候就已经推出了,并且在JDK1.5的时候将其功能进行扩充,主要就是增加了泛型,在Enumeration接口里只定义了两个方法:
判断是否有下一个元素:public Boolean hasMoreElements()
取得当前元素:public E nextElement()
不过要想取得Enumeration的实例化对象,不能依靠Collection接口了,之能够依靠Vector类完成,在Vector子类之中定义了一个方法:public Enumeration elements()。
使用Enumertaion进行输出
package com.day17.demo; import java.util.Enumeration; import java.util.Vector; public class IteratorTest { public static void main(String[] args) { // TODO 自动生成的方法存根 Vector<String> all=new Vector<String>(); all.add("hello"); all.add("hello"); all.add("world"); Enumeration<String> ite=all.elements(); while(ite.hasMoreElements()){ String str=ite.nextElement(); System.out.println(str); } } }
从开发而言,首先考虑的绝对不是Enumeration,考虑的肯定是Iterator,只有在必须使用的时候才用它。
1.5.4 JDK1.5的支持foreach
对于foreach输出除了可以进行数组的输出以外,还可以进行集合的输出。
使用foreach
package com.day17.demo; import java.util.ArrayList; import java.util.List; public class IteratorTest { public static void main(String[] args) { // TODO 自动生成的方法存根 List<String> all=new ArrayList<String>(); all.add("hello"); all.add("hello"); all.add("world"); for(String x : all){ System.out.println(x); } } }
使用foreach并不是一个被广泛认可的操作代码形式。