目录
3.Set接口-不允许重复元素(Set接口就是value值相同的Map接口,先有Map才有Set)
3.2TreeSet-有序存储-底层基于红黑树-(TreeMap)
4.1迭代输出:Iterator(只有Collection接口有)--只能从前向后输出(Collection接口提供)
4.3Enumeration(JDK1.0)枚举输出-Vector类支持
1.Java类集简介
类集实际上就属于动态数组,因为数组本身有一个最大的缺陷(长度是固定的)。从JDK1.2开始,Java提供了动态的对象数组实现框架--Java类集框架。
1.1Collection接口
Collection接口的定义如下:
public interface Collection<E> extends Iterable<E>
此接口常用的方法有如下几个:
public boolean add(E e) //向集合中添加数据
public boolean addAll(Collection<? extends E> c) //向集合中添加一组数据
public void clear() //清空集合数据
public boolean contains(Object o) //查找数据是否存在,需要使用equals()方法
public boolean remove(Object o) //删除数据,需要equals()方法
public int size() //取得集合长度
public Object[] toArray() //将集合变为对象数据返回
public Iterator<E> iterator() //取得Iterator接口对象,用于集合输出
Collection接口无法判断元素是否重复,所以经常使用它的子接口。Collection接口有两个使用频率很高的子接口List(允许重复元素)、Set(不允许重复元素)
2.List接口-允许元素重复
List接口中重要的扩展方法:
public E get(int index):根据索引取得数据
public E set(int index, E element):根据指定索引修改相应元素
2.1ArrayList(优先考虑)
接口中保存自定义类对象,自定义类必须覆写equals()方法;因为类集contains()、remove()等方法需要调用equals()来判断元素是否相等。
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if(obj == null){
return false;
}
if(!(obj instanceof Person)){
return false;
}
if(this == obj){
return true;
}
Person per = (Person) obj;
return this.age == per.age && this.name.equals(per.name);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test2{
public static void main(String[] args) throws Exception {
Person per1 = new Person("张三",18);
Person per2 = new Person("李四",20);
Person per3 = new Person("王五",40);
List<Person> list = new ArrayList<>();
list.add(per1);
list.add(per2);
list.add(per3);
System.out.println(list.contains(new Person("李四",20)));
list.remove(new Person("王五",40));
System.out.println(list);
}
}
底层实现是一个对象数组,声明一个ArrayList对象时,长度为0,当数组长度不够用时,扩容策略变为原来数组的1.5倍,对集合增删改查都是异步处理,性能较高,线程不安全。
2.2Vector
底层实现是一个对象数组,声明一个Victor对象时,初始化对象数组为10,当数组长度不够用时,扩容策略变为原来数组的2倍,对集合的修改都采用同步处理(直接在方法上使用内建锁),性能较低,线程安全。
2.3ArrayList与Victor的区别:
I.产生版本:
Vector是JDK1.0,ArrayList是JDK1.2产生
II.线程安全:
Vector采用在方法上添加synchronized来保证线程安全,性能较低;ArrayList采用异步处理,性能较高,线程不安全。
III.初始化以及扩容策略:
Vector对象产生时就初始化数组大小为10,当数组长度不够用时,扩容为源数组的2倍;ArrayList使用懒加载策略,在第一次添加元素时才初始化数组大小,当数组不够用时,扩容策略为源数组的1.5倍。
2.4LinkedList-基于链表实现的动态数组
大量访问数据用数组,大量有数据插入、修改用链表
3.Set接口-不允许重复元素(Set接口就是value值相同的Map接口,先有Map才有Set)
Set接口并没有对Collection接口进行扩充,方法与Collection完全一致,因此,在Set接口中没有get()方法。常用两个子类HashSet、TreeSet
3.1HashSet-无序存储
(根据对象的哈希码计算,但是哈希码我们不知道,所以为无序存储)
不允许元素重复,并且无序存储(根据hash码来保存元素),允许存放null。
public class Test2{
public static void main(String[] args) {
Set<String> set = new HashSet<>();
set.add("hello");
set.add("hello");
set.add("world");
set.add("java");
set.add(null);
System.out.println(set);
}
}
3.2TreeSet-有序存储-底层基于红黑树-(TreeMap)
不允许元素重复,并且按照升序排序,不允许存放null。
public class Test2{
public static void main(String[] args) {
Set<String> set = new TreeSet<>();
set.add("hello");
set.add("hello");
set.add("world");
set.add("java");
set.add(null);
System.out.println(set);
}
}
public class Test2{
public static void main(String[] args) {
Set<String> set = new TreeSet<>();
set.add("hello");
set.add("hello");
set.add("world");
set.add("java");
System.out.println(set);
}
}
3.3TreeSet排序分析
如果要进行对象数组的排序,对象所在的类一定要实现Compareable接口并且覆写compareTo()方法。
public int compareTo(T o)
返回值三种情况:
返回正数:表示当前对象大于目标对象
返回0:表示当前对象等于目标对象
返回负数:表示当前对象小于目标对象
class Person implements Comparable<Person>{
private String name;
private Integer 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 compareTo(Person o) {
return this.age-o.age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test2{
public static void main(String[] args) {
Person person1 = new Person();
person1.setAge(22);
person1.setName("Jack");
Person person2 = new Person();
person2.setAge(23);
person2.setName("Tom");
Person person3 = new Person();
person3.setAge(18);
person3.setName("Alex");
Set<Person> set = new TreeSet<>();
set.add(person1);
set.add(person2);
set.add(person3);
System.out.println(set);
}
}
3.4重复元素判断
在使用TreeSet子类进行数据保存的时候,重复元素的判断依靠的Comparable接口完成的。但是这并不是全部Set接口判断重复元素的方式,因为如果使用的是HashSet子类,由于其跟Comparable没有任何关系,所以它判断重复元素的方式依靠的是Object类中的两个方法:
//hash码
public native int hashCode()
//对象比较
public boolean equals(Object obj)
例:覆写hashCode()与equals()方法消除重复
class Person implements Comparable<Person> {
private String name ;
private Integer age ;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
Person person = (Person) o;
return Objects.equals(name, person.name) && Objects.equals(age, person.age);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public int compareTo(Person o) {
if (this.age > o.age ) {
return 1 ;
}else if (this.age < o.age ){
return -1 ;
}else {
return this.name.compareTo(o.name) ;
}
}
public Person(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;
}
}
public class Test {
public static void main(String[] args) {
Set<Person> set = new HashSet<>() ;
set.add(new Person("张三",20)) ;
// 重复元素
set.add(new Person("张三",20)) ;
set.add(new Person("李四",20)) ;
set.add(new Person("王五",19)) ;
System.out.println(set) ;
}
}
注意:若两个对象equals方法返回true,它们的hashCode必然要保证相等。但是两个对象的hashCode相等,equals不一定相等。当且仅当equals与hashCode方法均返回true,才认为两个对象真正相等。
4.集合输出(迭代器输出)
迭代器:为了遍历集合而生。
在之前进行集合输出的时候都利用了toString(),或者利用了List接口中的get()方法。这些都不是集合标准的输出。集合输 出一共有四种手段:Iterator、ListIterator、Enumeration、foreach。
4.1迭代输出:Iterator(只有Collection接口有)--只能从前向后输出(Collection接口提供)
对于Iterator接口最初的设计里面实际有三个抽象方法:
//1.判断是否有下一个元素
public boolean hasNext()
//2.取得当前元素
public E next()
//3.删除元素(此方法从JDK1.8开始变为default完整方法)
public default void remove()
例:
public class Test3 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("bit");
list.add("hello");
list.add("java");
//取得ArrayList的迭代器
Iterator<String> iterator = list.iterator() ; // 实例化Iterator对象
while (iterator.hasNext()) {
System.out.println(iterator.next()) ;
}
}
}
fail-fast机制:
public class Test3 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
Collections.addAll(list,"A","B","C","D");
//modCount = 6
//取得集合迭代器(取得当前集合的副本)
//new Itr();
//expectedModCount = 6
Iterator<String> iterator = list.iterator();
while(iterator.hasNext()){
//调用checkForComodification检查当前副本中的expectedModCount是否等于
//集合的modCount
String str = iterator.next();
if(str.equals("B")){
//调用集合类提供的remove()
//modCount = 7
list.remove("B");
continue;
}
System.out.println(str);
}
}
}
如果将其改为:
if(str.equals("B")){
iterator.remove();
continue;
}
ConcurrentModificationException发生在Collection集合使用迭代器遍历时,使用了集合类提供的修改集合内容而报错。而如果使用Iterator迭代器的remove()不会出现错误。
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
Collection集合中modCount表示当前集合修改的次数,expectedModCount是迭代器中记录当前集合的修改次数。
当取得集合迭代器时(即调用list.iterator()),expectedModCount = modCount,换言之,迭代器就是当前集合的一个副本。
快速失败策略保证了所有用户在进行迭代遍历集合时,拿到的数据一定是最新的数据。(避免“脏读”产生)。
总结:以后在迭代器遍历时,不要修改集合内容
4.2双向迭代接口:ListIterator
此接口定义的方法:
//1.判断是否有上一个元素
public boolean hasPrevious()
//2.取得上一个元素
public E previous()
Iterator接口对象是由Collection接口支持的,但是ListIterator是由List接口支持的,List接口提供有如下方法:
//取得ListIterator接口对象
public ListIterator listIterator()
例:
public class Test3 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("hello");
list.add("a");
list.add("hello");
list.add("java");
ListIterator<String> listiterator = list.listIterator() ;
System.out.println("从前向后输出:");
while(listiterator.hasNext()){
System.out.print(listiterator.next()+" ");
}
System.out.println("\n从后向前输出:");
while(listiterator.hasPrevious()){
System.out.print(listiterator.previous()+" ");
}
}
}
注意:如果要想实现由后向前的输出,那么首先应该从前向后的输出,否则无法实现双向
4.3Enumeration(JDK1.0)枚举输出-Vector类支持
观察此接口定义的方法:
//判断是否有下一个元素
public boolean hasMoreElements()
//取得元素
public E nextElement()
但是要想取得这个接口的实例化对象,是不能依靠Collection、List、Set等接口的。只能够依靠Vector子类,因为Enumeration最早的设计就是为Vector服务的,在Vector类中提供有一个取得Enumeration接口对象的方法:
//取得Enumeration接口对象:
public Enumeration elements()
public class Test3 {
public static void main(String[] args) {
Vector<String> vector = new Vector<>();
vector.add("hello");
vector.add("a");
vector.add("hello");
vector.add("java");
Enumeration<String> enumeration = vector.elements();
while(enumeration.hasMoreElements()){
System.out.println(enumeration.nextElement());
}
}
}
4.4foreach输出
从JDK1.5开始foreach可以输出数组,能使用foreach输出的本质在于各个集合类都内置了迭代器
例:
public class Test3 {
public static void main(String[] args) {
Vector<String> vector = new Vector<>();
vector.add("hello");
vector.add("a");
vector.add("hello");
vector.add("java");
for(String str : vector){
System.out.println(str);
}
}
}
5.Map集合
Map接口是Java中保存二元偶对象(键值对)的最顶层接口
//key值唯一,通过一个key值一定能唯一找到一个value值。
public interface Map<K,V>
Map接口的核心方法:
public V put(K key, V value) //向Map中添加数据
public V get(K key) //根据指定的key值取得相应的value值,若没有此key值,返回null
public Set<Map.Entry<K,V>> entrySet() //将Map集合变为Set集合。
public Set<K> keySet() //返回所有key值集合,key不能重复
public Collection<V> values() //返回所有value值,value可以重复
Map本身是一个接口,要使用Map需要通过子类进行对象实例化。Map接口的常用子类有如下四个:HashMap、Hashtable、TreeMap、ConcurrentHashMap。
5.1HashMap子类(常用)
例:
public class Test3 {
public static void main(String[] args) {
Map<Integer,String> map = new HashMap<>();
map.put(1,"hello");
//当key值重复,再次put变为相应的value的更新操作
map.put(1,"Hello");
map.put(2,"java");
System.out.println(map.get(1));
System.out.println(map.get(99));
}
}
例:取得Map中所有key信息
public class Test3 {
public static void main(String[] args) {
Map<Integer,String> map = new HashMap<>();
map.put(1,"hello");
//当key值重复,再次put变为相应的value的更新操作
map.put(1,"Hello");
map.put(2,"java");
map.put(3,"world");
Set<Integer> set = map.keySet();
Iterator<Integer> iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
总结:(HashMap类比HashSet)
1.允许key和value为null,且key值有且只有一个为空,value可以有任意多个为null
2.JDK1.2产生
3.异步处理,效率高,线程不安全
4.底层哈希表+红黑树(JDK8)
5.2Hashtable
JDK1.0提供有三大主要类:Vector、Enumeration、Hashtable。Hashtable是最早实现这种二元偶数据结构。
例:
public class Test3 {
public static void main(String[] args) {
Map<Integer,String> map = new Hashtable<>();
map.put(1,"hello");
//当key值重复,再次put变为相应的value的更新操作
map.put(1,"Hello");
map.put(2,"java");
map.put(3,"world");
System.out.println(map);
}
}
总结:
1.key值与value均不为null
2.JDK1.0产生
3.使用方法上加锁,效率低,线程安全
4.底层哈希表
5.3HashMap与Hashtable的区别:
NO | 区别 | HashMap | Hashtable |
1 | 退出版本 | JDK1.2 | JDK1.0 |
2 | 性能 | 异步处理,性能高 | 同步处理、性能较低 |
3 | 安全性 | 非线程安全 | 线程安全 |
4 | null操作 | 允许存放null(有且只有一个) | key与value都不为空,否则出现NullPointerException |
5.4Map集合使用迭代器(Iterator)输出
public Set<Map.Entry<K,V>> entrySet() //将Map集合变为Set集合
public class Test3 {
public static void main(String[] args) {
Map<Integer,String> map = new HashMap<>();
map.put(1,"hello");
map.put(2,"java");
map.put(3,"world");
//1.将Map集合变为Set集合
Set<Map.Entry<Integer,String>> set = map.entrySet();
//2.获取Iterator对象
Iterator<Map.Entry<Integer,String>> iterator = set.iterator();
//3.输出
while(iterator.hasNext()){
//4.取出每一个Map.Entry对象
Map.Entry<Integer,String> entry = iterator.next();
//5.取得key与value
System.out.println(entry.getKey()+"="+entry.getValue());
}
}
}
6.栈与队列
6.1栈(Stack):先入后出
栈的实现:
class Stack<E> extends Vector<E> //所以是动态数组
一些方法如下:
push() //入栈
pop() //出栈
peek() //返回栈顶元素但不出栈
public class Test3 {
public static void main(String[] args) {
Stack<String> stack = new Stack<>();
stack.push("A");
stack.push("B");
stack.push("C");
System.out.println(stack.peek());
System.out.println(stack.pop());
System.out.println(stack.pop());
System.out.println(stack.pop());
//此时栈已经空
System.out.println(stack.pop());
}
}
注意:如果栈已经空了,再次出栈就会空栈异常
6.2队列(Queue)--先进先出
Queue接口有一个子类LinkedList,在这个接口中有如下方法:
public E poll() //按照队列取出内容
例:
public class Test3 {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<>();
queue.add("A");
queue.add("B");
queue.add("C");
System.out.println(queue.peek());
System.out.println(queue.poll());
System.out.println(queue.poll());
System.out.println(queue.poll());
//取完数据继续poll返回null
System.out.println(queue.poll());
}
}
7.资源文件操作(Properties属性文件)
class Properties extends Hashtable<Object,Object>
资源文件内容都是k-v格式,并且无论是key、value都是String类型,在进行属性操作的时候往往会使用Properties类提供的方法完成:
//设置属性
public synchronized Object setProperty(String key, String value)
//取得属性,如果没有指定的key,则返回null
public String getProperty(String key)
//取得属性,如果没有指定的key则返回默认值
public String getProperty(String key, String defaultValue)
例:观察属性操作
public class Test3 {
public static void main(String[] args) {
Properties properties = new Properties();
properties.setProperty("shanxi","xi'an");
properties.setProperty("sh","shanghai");
System.out.println(properties.getProperty("shanxi"));
System.out.println(properties.get("bj"));
System.out.println(properties.getProperty("bj","beijing"));
}
}
在Properties类中提供有IO支持的方法:
//保存属性
public void store(OutputStream out, String comments) throws IOException
//读取属性
public synchronized void load(InputStream in) throws IOException
注意:Properties只能操作String
8.工具类
Collections是一个集合操作的工具类,包含有集合反转、排序等操作。
8.1将线程不安全集合包装为线程安全集合
在add、remove等修改方法中使用同步代码块保证线程安全,效率较低。要使用线程安全集合,推荐使用juc包下的并发集合类(ConcurrentHashMap、CopyOnWriterArrayList)
8.2集合排序
Collections.sort(集合名称)
8.3集合反转
Collections.reverse(集合反转)
例:
public class Test3 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
Collections.addAll(list,"A","C","B");
//将集合转为线程安全集合
List<String> listSafe = Collections.synchronizedList(list);
//集合排序
Collections.sort(listSafe);
for(String str : listSafe){
System.out.println(str);
}
System.out.println("-------------");
Collections.reverse(listSafe);
for(String str : listSafe){
System.out.println(str);
}
}
}
9.Stream数据流(JDK1.8新增)
9.1Collection接口改进
从JDK1.8开始,Collection接口里面除了定义一些抽象方法外,也提供了一些普通方法:
//1.forEach()输出支持
default void forEach(Consumer<? super T>action)
//2.取得Stream数据流对象
default Stream stream()
例:使用forEach输出
public class Test3 {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
Collections.addAll(list, 1,3,5,7,9);
list.forEach(System.out::println);
}
}
9.2Stream操作
例:filter数据过滤
public class Test3 {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
Collections.addAll(list, 1,3,5,7,9);
//取得Stream对象
Stream<Integer> stream = list.stream();
//统计出集合中所有偶数个数
System.out.println(stream.filter(e -> e % 2 == 0).count());
}
}
例:取得最大最小值:max/min
public class Test3 {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
Collections.addAll(list, 1,3,5,7,9);
//取得Stream对象
Stream<Integer> stream = list.stream();
//统计出集合中最大的数
System.out.println(stream.max(Integer::compareTo).get());
}
}