javaSE-集合

一.类图

collection

image-20200804085102081

上图中的Iterator接口在1.5之后就由Iterable接口代替了,Iterable接口封装了一个方法叫做iterator,该方法返回一个iterator对象,而每个iterator接口的实现类都分别写在了各个集合类的实现类中,以内部类方式实现iterator接口

用ArrayList举个例子:

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable{
    //实现了iterator接口的内部类
    private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

        Itr() {} 
    }
    
    //实现了Iterable接口中的iteartor方法
	public Iterator<E> iterator() {
        return new Itr();
    }
}

map

image-20200804090411660

二.Collection接口

1.概述

该接口是所有单元素存储的总接口(List接口和Set接口的父接口),那个Iterable可以理解为Collection的一个附属接口,虽然它是Collection的父接口,但是整个集合的主要功能定义都是在Collection中定义的,Iterable这个接口就提供了个iterator()方法供所有实现类获取迭代器.所以Collection是核心接口

接下来看看它提供的方法:

2.Collection接口中的方法

方法解释
boolean add(Object o)该方法用于向集合里添加一个元素,添加成功返回true
void clear()清除集合里的所有元素,将集合长度变为0
boolean contains(Object o)返回集合里是否包含指定元素
boolean containsAll(Collection c)返回集合里是否包含集合c里的所有元素
int hashCode()返回此collection的哈希码值
boolean isEmpty()返回集合是否为空,当集合长度为0时,返回true
Iterator iterator()返回一个Iterator对象,用于遍历集合里的元素
boolean remove(Object o)删除集合中指定元素o,当集合中包含了一个或多个元素o时,这些元素将被删除,该方法将返回true
boolean removeAll(Collection c)从集合中删除集合c里包含的所有元素,如果删除了一个或一个以上的元素,返回true
boolean retainAll(Collection c)从集合中删除不在集合c里包含的元素,如果删除了一个或一个以上的元素,返回true
int size()返回集合中的元素个数
Object[] toArray()该方法把集合转换成一个数组,所有集合元素变成对应的数组元素

三.List接口及其实现类

1.概述

List是有序可重复的,底层的数据结构是线性结构

2.方法

List不单单继承了Collection的方法,还增加了一些新的方法。

方法解释
void add(int index, Object element)将元素element插入到List的index处
boolean addAll(int index, Collection c)将集合c所包含的所有元素都插入在List集合的index处
Object get(int index)返回集合index处的元素
int indexOf(Object o)返回对象o在List集合中出现的位置索引
int lastIndexOf(Object o)返回对象o在List集合中最后一次出现的位置索引
Object remove(int index)删除并返回index索引处的元素
Object set(int index, Object element)将index索引处的元素替换成element对象,返回新元素

3.实现类

ArrayList : 底层数据结构是数组,查询和修改效率高,删除添加效率低

LinkedList : 底层数据结构是双向链表,删除添加效率高,查询和修改效率低

Vector : 类似于ArrayList,它是线程安全的,但是现在一般线程安全问题都另外处理了,所以这个也就被弃用了

Stack: 继承Vector实现的栈,栈结构是先进后出,但已被LinkedList取代

4.LinkedList新增方法

方法解释
void addFirst(Object e)将指定元素插入该集合的开头
void addLast(Object e)将指定元素插入该集合结尾
boolean offerFirst(Object e)将指定元素插入该集合的开头,成功返回true
boolean offerLast(Object e)将指定元素插入该集合结尾
boolean offer(Object e)将指定元素插入该集合结尾
Object getFirst()获取,但不删除集合第第一个元素
Object getLast()获取,但不删除集合最后一个元素
Object peekFirst()获取,但不删除该集合第一个元素,如果集合为空,则返回null
Object peekLast()获取,但不删除该集合最后一个元素,如果集合为空,则返回null
Object pollFirst()获取,并删除该集合第一个元素,如果集合为空,则返回null
Object pollLast()获取,并删除该集合最后一个元素,如果集合为空,则返回null
Object removeFirst()获取,并删除该集合的第一个元素
Object removeLast()获取,并删除该集合的最后一个元素
Object pop()pop出该集合的第一个元素
void push(Object e)将一个元素push到集合

四.Set接口及其实现类

1.概述

Set是无序不可重复的,其中无序是指添加的顺序和取出来的顺序无序,存储的不是线性结构

2.方法

方法解释
boolean add(E e)如果此set中尚未包含指定元素,则添加指定元素
boolean isEmpty()如果此set不包含任何元素,则返回true
boolean contains(Object o)如果此set包含指定元素,则返回 true
boolean remove(Object o)如果指定元素存在于此set中,则将其移除
int size()返回此set中的元素的数量
void clear()从此set中移除所有元素

3.实现类

HashSet :

它的底层是由HashMap实现的,这个map是一个仅有key的map(value是一个常量对象,用来占位,没有什么意义)

当这个key重复的时候,add方法不会添加这个重复的元素

TreeSet

它的底层是由TreeMap实现的,可排序,默认自然升序.

五.Collections工具类和Comparable、Comparator比较器

1.Collections工具类

Collections是一个包装工具类。它包含有各种有关集合操作的静态多态方法,此类不能实例化,服务于Java的Collection接口。

常用方法:

sort、reverse、fill、copy、max、min、swap等

重点: sort排序

public static <T extends Comparable<? super T>> void sort(List<T> list)

这个方法的意思就是sort传入的是一个List接口下的实现类对象,而且这个泛型还要求要比较的元素必须是实现了Comparable这个接口或者是它的子类

  1. 根据元素的自然顺序 对指定列表按升序进行排序。列表中的所有元素都必须实现 Comparable 接口。
  2. 此外,列表中的所有元素都必须是可相互比较的(也就是说,对于列表中的任何 e1 和 e2 元素,e1.compareTo(e2) 不得抛出 ClassCastException)。

2.Comparable、Comparator比较器

对象排序,就是比较大小,要实现Comparable或Comparator比较器之一,才有资格做比较排序。
【介绍】

  1. Comparable:与对象紧相关的比较器,可以称“第一方比较器”。
  2. Comparator:此为与具体类无关的第三方比较器。

例子:

  1. 定义一个学生类,实现Comparable接口,使用学号进行Collections.sort排序

  2. 定义一个学生类,实现Comparator接口,使用学号进行Collections.sort排序

学生类

package test06;

public class Student implements Comparable<Student>{
	private String name;
	private String sex;
	private int age;
	private int stuNo;
	@Override
	public int compareTo(Student o) {
		// TODO Auto-generated method stub
		//按照学号升序排序
//		return this.stuNo-o.stuNo;
		//降序
//		return o.stuNo-this.stuNo;
		//多条件排序(首先按照年龄升序,然后按照学号降序)
//		if(this.age-o.age==0){
//			return o.stuNo-this.stuNo;
//		}
//		return this.age-o.age;
		//按照姓名排序
		return this.name.compareTo(o.name);
	}

	public Student() {
		super();
	}

	public Student(String name, String sex, int age, int stuNo) {
		super();
		this.name = name;
		this.sex = sex;
		this.age = age;
		this.stuNo = stuNo;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getSex() {
		return sex;
	}

	public void setSex(String sex) {
		this.sex = sex;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public int getStuNo() {
		return stuNo;
	}

	public void setStuNo(int stuNo) {
		this.stuNo = stuNo;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "Student [name=" + name + ", sex=" + sex + ", age=" + age
				+ ", stuNo=" + stuNo + "]";
	}

}

测试类:

public class TestComparable {
	public static void main(String[] args) {
		//创建四个学生
		Student s1 = new Student("tom","男",20,1807001);
		Student s2 = new Student("jack","男",20,1807022);
		Student s3 = new Student("jeny","女",22,1807011);
		Student s4 = new Student("lady","女",21,1807031);
		
		List<Student> stuList = new ArrayList<Student>();
		stuList.add(s1);
		stuList.add(s2);
		stuList.add(s3);
		stuList.add(s4);
		System.out.println("排序前=====");
		for(Student s:stuList){
			System.out.println(s);
		}
		Collections.sort(stuList);
		System.out.println("排序后=====");
		for(Student s:stuList){
			System.out.println(s);
		}
	}
}

输出结果:

排序前=====
Student [name=tom, sex=, age=20, stuNo=1807001]
Student [name=jack, sex=, age=20, stuNo=1807022]
Student [name=jeny, sex=, age=22, stuNo=1807011]
Student [name=lady, sex=, age=21, stuNo=1807031]
排序后=====
Student [name=jack, sex=, age=20, stuNo=1807022]
Student [name=jeny, sex=, age=22, stuNo=1807011]
Student [name=lady, sex=, age=21, stuNo=1807031]
Student [name=tom, sex=, age=20, stuNo=1807001]

六.Map接口

1.概述

Map用于保存具有映射关系的数据,因此Map集合里保存两组值。

  1. 一组值用于保存key,一组值用于保存value

  2. key~value之间存在单向一对一关系,通过指定key可以找到唯一的value值

  3. key和value都可以是任何引用类型对象

  4. 允许存在value为null,但是只允许存在一个key为null

2.常用方法

方法解释
V put(K key, V value)将指定的值与此映射中的指定键关联
boolean containsKey(Object key)如果此映射包含指定键的映射关系,则返回true
boolean containsValue(Object value)如果此映射将一个或多个键映射到指定值,则返回true
boolean isEmpty()如果此映射未包含键-值映射关系,则返回true
V get(Object key)返回指定键所映射的值,如果此映射不包含该键的映射关系,则返回null
Set keySet()返回此映射中包含的键的set集合
Collection values()返回此映射中包含的值的Collection集合
Set<Map.Entry<K,V>> entrySet()返回此映射中包含的映射关系的set集合
boolean equals(Object o)返回指定的对象与此映射是否相等
int hashCode()返回此映射的哈希码值
V remove(Object key)如果存在一个键的映射关系,则将其从此映射中移除

七.HashMap类

1.概述

特点:key无序不可重复,底层是哈希表

2.哈希表实现原理

  • HashMap实际上是一个"链表的数组"的数据结构,每个元素存放链表头结点的数组,即数组和链表的结合体。

  • 当我们往HashMap中put元素的时候,先根据key的hashCode重新计算hash值,根据hash值得到这个元素在数组中的位置(即下标),如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。

  • 只有相同的hash值的两个值才会被放到数组中的同一个位置上形成链表。

  • 如果这两个Entry的key通过equals比较返回true,新添加Entry的value将覆盖集合中原有Entry的value,但key不会覆盖。

    img

3.总结

  • HashMap中key的hashCode值决定了<k,v>键值对组成的entry在哈希表中的存放位置。
  • HashMap中key通过equals()比较,确定是覆盖key对应的value值还是在链表中添加新的entry。
  • 综合前两条,需要重写hashCode()和equals()方法。

八.TreeMap类

1.概述

TreeMap是SortedMap接口的实现类,可以根据Key进行排序,HashMap没有这个功能。
【特点】

  1. 底层由可排序二叉树实现

  2. 不指定比较器默认按照key自然升序,指定比较器按照比较器排序

2.遍历TreeMap的两种方法

使用KeySet

//使用keySet方式输出
Set<Integer> keySet = treemap.keySet();
		for(Integer i: keySet){
			System.out.println("key--->"+i+"   value--->"+treemap.get(i));
		}

使用EntrySet

//使用entrySet方式输出
		Set<Map.Entry<Integer, Student>> entryset = treemap.entrySet();
		for(Map.Entry<Integer, Student> entry:entryset){
			System.out.println("key--->"+entry.getKey()+"   value--->"+entry.getValue());
		}

区别:

使用KeySet获取的是TreeMap中所有的key

使用EntrySet获取的是TreeMap中所有的键值对

3.为什么keySet和entryset输出后就是排好序的

package day23;

import java.util.*;


/**
 * 作业2:treemap的keyset方式或是entryset方式如何保证的有序输出。
 * @author  付君华
 * @Date 2021年6月29日
 * @Description
 */
public class Hw02 {
	
	/*
	 分析keySet
	 public Set<K> keySet() {
        return navigableKeySet();
     }
     上面的调用了下面的方法
     public NavigableSet<K> navigableKeySet() {
        KeySet<K> nks = navigableKeySet;
        return (nks != null) ? nks : (navigableKeySet = new KeySet<>(this));
    }
    返回了一个NavigableSet 这个是SortedSet下的一个子接口 而这个KeySet是NavigableSet的实现类
    
    判断nks是否为空,非空的话说明创建过了,不用重复出创建
    空的话创建一个KeySet
     看看这个KeySet具体代码	
     
     
	  
	  static final class KeySet<E> extends AbstractSet<E> implements NavigableSet<E> {
        private final NavigableMap<E, ?> m;
        KeySet(NavigableMap<E,?> map) { m = map; }

        public Iterator<E> iterator() {
            if (m instanceof TreeMap)
                return ((TreeMap<E,?>)m).keyIterator();
            else
                return ((TreeMap.NavigableSubMap<E,?>)m).keyIterator();
        }
    }
    
    主要看这个构造方法
    static final class KeySet<E> extends AbstractSet<E> implements NavigableSet<E> {
        private final NavigableMap<E, ?> m;
        KeySet(NavigableMap<E,?> map) { m = map; }
	  
	  
	 首先是传入了个map,这个map也就是treemap,也就是调用keySet方法的的treemap
	 
	 但是也没有进行排序啊,就创建了个对象,咋就排序了呢?
	 百度查了一下资料,网上说是在遍历的时候才会进行排序
	 so 嘎
	 那如果用普通for循环的方式来遍历是否就不会进行排序了呢?
	 
	 不行,这KeySet中就没get方法,只能用增强for循环,只要一用这个增强for,那就会调用KeySet中的迭代器
	 然后进行排序,返回迭代器咋就排序了呢? 
	 两种可能,一是for循环给实现的,而是迭代器实现的
	 找一下增强for的源码:
	 for(Iterator iterator = list.iterator(); iterator.hasNext(); System.out.println(i)){
   		i = (Integer)iterator.next();        
	 }
	 很明显不是for实现的
	 那只能是迭代器实现的了
	 
	 那看看迭代器
	 
	 上面for中调用的是无参的迭代器方法,所以应该是
	  
	  public Iterator<E> iterator() {
            if (m instanceof TreeMap)
                return ((TreeMap<E,?>)m).keyIterator();
            else
                return ((TreeMap.NavigableSubMap<E,?>)m).keyIterator();
        }
        
      如果是个treemap 那么就返回一个treemap 的iterator 否则返回那个玩意的iterator
      
      那再看看keyIterator
      Iterator<K> keyIterator() {
        return new KeyIterator(getFirstEntry());
      }
      
      final class KeyIterator extends PrivateEntryIterator<K> {
        KeyIterator(Entry<K,V> first) {
            super(first);
        }
        public K next() {
            return nextEntry().key;
        }
       }
       
       final Entry<K,V> getFirstEntry() {
        Entry<K,V> p = root;
        if (p != null)
            while (p.left != null)
                p = p.left;
        return p;
      }
      
      也就是将第一个entry传入到了KeyIterator的构造方法中
      这个构造方法将这个传到了父类的构造中
      
      PrivateEntryIterator(Entry<K,V> first) {
            expectedModCount = modCount;
            lastReturned = null;
            next = first;
        }
        
      应该快找到答案了
      
      主要看看这个类中的实现
      
      
      abstract class PrivateEntryIterator<T> implements Iterator<T> {
        Entry<K,V> next;
        Entry<K,V> lastReturned;
        int expectedModCount;

        PrivateEntryIterator(Entry<K,V> first) {
            expectedModCount = modCount;
            lastReturned = null;
            next = first;
        }

        public final boolean hasNext() {
            return next != null;
        }

        final Entry<K,V> nextEntry() {
            Entry<K,V> e = next;
            if (e == null)
                throw new NoSuchElementException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            next = successor(e);
            lastReturned = e;
            return e;
        }

        final Entry<K,V> prevEntry() {
            Entry<K,V> e = next;
            if (e == null)
                throw new NoSuchElementException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            next = predecessor(e);
            lastReturned = e;
            return e;
        }

        public void remove() {
            if (lastReturned == null)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            // deleted entries are replaced by their successors
            if (lastReturned.left != null && lastReturned.right != null)
                next = lastReturned;
            deleteEntry(lastReturned);
            expectedModCount = modCount;
            lastReturned = null;
        }
    }
    
    主要看他的next方法,怎么就能排序输出呢?
    
    next = successor(e);
    
    这里调用了一个方法
    
    static <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) {
        if (t == null)
            return null;
        else if (t.right != null) {
            Entry<K,V> p = t.right;
            while (p.left != null)
                p = p.left;
            return p;
        } else {
            Entry<K,V> p = t.parent;
            Entry<K,V> ch = t;
            while (p != null && ch == p.right) {
                ch = p;
                p = p.parent;
            }
            return p;
        }
    }
    
    这个百度了一下是获取后继节点的意思,但是也没实现排序啊
    
    ..
    恍然大悟
    在创建treemap的时候就已经是排好序的了(红黑二叉树)
    然后获取节点的时候就是有序获取的,所以这个keySet就是有序的
    
    那entry也就是一样的道理了
    
    
    总结一下:
    
    keySet和entrySet方法返回了一个set集合
    
    这个set集合中的元素在创建treemap的时候就已经排好序了
    
    上面的方法在调用的时候用到了treemap,然后这俩方法就将这个map保存到了一个内部类中
    
    在遍历的时候,调用迭代器,然后将这个treemap的一个一个值给输出   
	  
	 */
	
	
	
	
	public static void main(String[] args) {
		//创建一个TreeMap
		Map<Integer, Student> treemap = new TreeMap<Integer, Hw02.Student>();
		//创建学生数据
		Student s1 = new Student("zhagnsan", 20, 2021001);
		Student s2 = new Student("lisi", 20, 2021002);
		Student s3 = new Student("wangwu", 20, 2021003);
		//将数据添加到Map中
		treemap.put(s1.getStdNo(),s1);
		treemap.put(s2.getStdNo(),s2);
		treemap.put(s3.getStdNo(),s3);
		//使用keySet方式输出
		Set<Integer> keySet = treemap.keySet();
		for(Integer i: keySet){
			System.out.println("key--->"+i+"   value--->"+treemap.get(i));
		}
		
		//使用entrySet方式输出
		Set<Map.Entry<Integer, Student>> entryset = treemap.entrySet();
		for(Map.Entry<Integer, Student> entry:entryset){
			System.out.println("key--->"+entry.getKey()+"   value--->"+entry.getValue());
		}
		
		
	}
	
	public static class Student {
		private String name;
		private int age;
		private int stdNo;
		
		@Override
		public int hashCode() {
			return Integer.valueOf(this.stdNo).hashCode()+this.name.hashCode();
		}

		@Override
		public boolean equals(Object obj) {
			Student stu=(Student)obj;
			return (this.stdNo==stu.stdNo)&&(this.name.equals(stu.name));
		}

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

		public Student(String name, int age, int stdNo) {
			super();
			this.name = name;
			this.age = age;
			this.stdNo = stdNo;
		}

		public String getName() {
			return name;
		}

		public void setName(String name) {
			this.name = name;
		}

		public int getAge() {
			return age;
		}

		public void setAge(int age) {
			this.age = age;
		}

		public int getStdNo() {
			return stdNo;
		}

		public void setStdNo(int stdNo) {
			this.stdNo = stdNo;
		}

	}

}

九.泛型

1.概述

泛型就是用来约束数据类型的一种语法,尤其是在集合与框架编写中常见

2.泛型的简单使用

List<String> list = new ArrayList<String>();//这个集合中就只能存储String类型

3.泛型的拓展使用

//在类上加泛型
class Stu<T>{
    public void m1(T t){
        System.out.println(t);
    }
}
//测试
class Main{
    public static void main(String args[]){
        //创建一个Stu类,带泛型
        Stu<String> s = new Stu<String>();
        //调用Stu中的m1方法
        s.m1("hello");//这只能传字符串
    }
}


//在方法上加泛型
public class Test {
	public static void main(String[] args) {
		m1("123");//输出123,这里传入什么类型都可以,相当于Object
	}
	
	public static <T> void m1(T t){
		
		System.out.println(t);
		
	}
}

4.extends关键字和super关键字

<T extends 类型 > 就是T是这个类型或子类

<T super 类型> 就是这个这个类型或父类

5.?符号

? 是指所有没有限定类型,T是限定了某一种类型

<? extends 类型> 所有这个类型或者这个类型的子类都可以
<? super 类型> 所有这个类型或者这个类型的父类
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Antgeek

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值