文章目录
集合的分类
-
Collection集合(单列集合的接口)
List接口集合(存取有序,集合可有重复值,有索引的) 实现类 ArrayList (底部通过数组) LinkedList(底部通过双向链表实现) Vector(线程安全 也是数组实现)--->Stack(线程安全的栈) set接口集合(存取无序,不可有重复值,无索引) 实现类 Hashset(底部通过哈希表)--->LinkedHashset(底部哈希表+链表) (无序,不可有重复值,无索引)(有序,不重复,无索引) TreeSet(底部通过红黑树) (默认按升序排列,不可有重复值,无索引)
-
Map集合(双列集合的接口 键值对的数据)
实现类 HashMap(哈希表) LinkedHashMap(哈希表+链表) TreeMap(红黑树)
-
Iterator迭代器
一般iterator 其他迭代器
集合的整体框架图
- 集合只能存储引用类型
一、Iterator接口(迭代器)
- 提供的方法
- Iterable 含有foreach.迭代器和一个分离器
- Iterator<T> iterator(); //迭代器
- void forEach(Consumer<? super T> action) //增强for循环
- void Spliterator<T> spliterator() //分离器
迭代器需要注意的地方
* 当使用迭代器遍历的时候一定要注意:
* 1.只要调用过一次next(),指针就会下移
* 2.当出现迭代器和集合并行调用时会出现并行异常
* throw ConcurrentModificationException //并行修改异常
* while (iterator.hasNext()){
* strings.remove(iterator.next());
* 有一种特殊情况会不报错
* 当前检查异常的方法 当modCount == expectedModCount时
* 就不会报错一般在倒数几个,和你使用next()次数有关
* final void checkForComodification() {
* if (modCount != expectedModCount)
* throw new ConcurrentModificationException();
* }
* 3.在想要删除元素时,可以通过迭代器自己的 iterator.remove();
* 4.想要使用add(),需要使用下层迭代器ListIterator
当需要在迭代器中进行插入操作的时候上层迭代器时没有add方法的,可以使用下层接口的迭代器
ListIterator<String> stringListIterator = list.listIterator();
while (stringListIterator.hasNext()){
stringListIterator.add("dsadfasf");
二、Collection接口
- 提供的方法
基本都是一些增删改查和流的方法
int size (); //大小
boolean isEmpty (); //判空
boolean contains (Object o); //包含
<T> T[]toArray(T[]a);//转数组
boolean add (E e); //添加
boolean remove (Object o); //删除
boolean containsAll (Collection < ? > c); //包含
boolean addAll (Collection < ? extends E > c); //添加
boolean removeAll (Collection < ? > c);
void clear (); //清除
default
Stream<E> stream () //顺序流
Stream<E> parallelStream () //并行流
遍历方式(五种)
//迭代器
Iterator<String> iterator = strings.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
//增强for(也是迭代器实现)
for (String str : strings){
System.out.println(str);
}
//foreach(都行)
strings.forEach(System.out::println);
strings.forEach((String s)->{
System.out.println(s);
});
strings.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
//fori(需要逻辑判断,因为数组删除,增加会自动向前或者向后一定一位,可能会造成误差,少读)
// 集合stream流(流不修改数据,只操作数据)
strings.stream().forEach(System.out::println);
List集合
- 通过接口List中的静态方法of()获取一个定长的集合 ,set.map接口都可以使用of()静态方法创建一个定长的数组
通过把他调用集合的构造方法实现批量输入(当放入调用集合,它就不会影响新集合是否定长)
ArrayList<String> strings1 = new ArrayList<>(List.of("a", "b", "c", "d"));
//批量输入还可以使用其他,比如Collections工具类
boolean b = Collections.addAll(new ArrayList<>(), "b", "c", "a"); //注意这里是一个boolean值
//当然还可以使用集合的addall()这类型的方式实现批量增加
//当然也可以使用Steam流的方式,对集合进行批量的添加
- List<String> a = List.of("a", "b", "c", "d");
- //这里用到了一个不定传入参数的方法
- static int getSum(int... a){ //a本质上是一个数组 //如果这个方法存在可变参数,一定要放在最后
int sum=0;
for (int i : a) {
sum+=i;
}
return sum;
}
-
- 提供的特有方法
基本都是和索引相关的方法
boolean addAll(int index, Collection<? extends E> c); //提供从位置插入所有
void replaceAll(UnaryOperator<E> operator) //替代所有
void sort(Comparator<? super E> c) //通过comparator进行排序
E get(int index) //根据下标获取元素
E set(int index, E element) //根据下标设置元素
void add(int index, E element); //根据下标增加元素
E remove(int index) //删除元素
int indexOf(Object o); //返回第一次出现数组下标
int lastIndexOf(Object o) //返回最后依次出现的下标
List<E> subList(int fromIndex, int toIndex);//返回之间的左包右不包
static <E> List<E> copyOf(Collection<? extends E> coll) //备份
ArrayList类
- 默认容量
当创建集合时是为一个空的数组;
向空数组加元素时
// 通过add()-->grow()
return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity
DEFAULT_CAPACITY = 10
- 当存入超过10时 自增策略
private Object[] grow(int minCapacity) {
int oldCapacity = elementData.length;
//当数组不为空还超出数组长度时
if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
{
int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity, /* minimum growth */
oldCapacity >> 1 //除二操作 /* preferred growth */);
//数组长度的自增是通过数组的cpoyof复制到新长度下的数组中,不是在后面添加长度,所以自增时非常耗时的操作当提前预知需要的多少的时候,可以提前给数组赋长度值
return elementData = Arrays.copyOf(elementData, newCapacity);
}
}
}
//新长度定义策略
public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
// preconditions not checked because of inlining
// assert oldLength >= 0
// assert minGrowth > 0
int prefLength = oldLength + Math.max(minGrowth, prefGrowth); // might overflow
if (0 < prefLength && prefLength <= SOFT_MAX_ARRAY_LENGTH) {
return prefLength;
} else {
// put code cold in a separate method
return hugeLength(oldLength, minGrowth);
}
}
//异常处理,健壮性
private static int hugeLength(int oldLength, int minGrowth) {
int minLength = oldLength + minGrowth;
if (minLength < 0) { // overflow
throw new OutOfMemoryError(
"Required array length " + oldLength + " + " + minGrowth + " is too large");
} else if (minLength <= SOFT_MAX_ARRAY_LENGTH) {
return SOFT_MAX_ARRAY_LENGTH;
} else {
return minLength;
}
}
- 提供的方法
void trimToSize() //修剪容量到当前容量大小
LinkedList
由于是双向链表的实现
提供的特有方法都和链表有关
public E removeFirst() //头部删除
public E removeLast() //尾部删除
public void addFirst(E e) //头插法
public void addLast(E e) //尾插法
Vector
是线程安全的list集合
它的增删改查都是加了sychronized锁的
public synchronized void removeElementAt(int index)
public synchronized void insertElementAt(E obj, int index)
public synchronized void addElement(E obj)
public synchronized E get(int index)
Set接口
存取无序,无索引,不可以存取重复值
当为自定义对象的时候,通过equals()方法和hashcode()进行两个对象的比较进行去重
HashSet Hash
底部实现:哈希
哈希表时通过数组加链表的方式来实现,8之后在链表长度超过8之后就会转化为红黑树,在减少的时候链表变为6转换为链表
//计算hash值之后膜16 ,就不能预知取出的顺序了取时按数组下标进行取
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
//HashSet创建对象时调用的是HashMap的构造方法
public HashSet() {
map = new HashMap<>();
}
//默认长度为16(第一次put分配)
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
//负载因子为0.75,扩容机制 当扩容因子超过阀值的时候将数组两倍扩容
static final float DEFAULT_LOAD_FACTOR = 0.75f;
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
//调用的add方法也是使用了Map集合中的put
public boolean add(E e) {
//以key值作为存储对象,value为空
return map.put(e, PRESENT)==null;
}
//当key存在的时候只会替换value,然后可以返回旧的value值
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
TreeSet
底部实现:红黑树
三.Map集合
map集合的put方法有添加和修改的功能
remove方法通过key值进行删除
提供ContainsValue,ContainsKey进行查询是否存在
双列集合
Key不重复,存取无序,无索引
Map的遍历
/*
Map集合的三种遍历方式
1.通过keySet
2.通过entrySet
3.通过foreach
*/
Set<String> strings = hm.keySet();
for (String string : strings) {
System.out.println(string+" "+hm.get(string));
}
Set<Map.Entry<String, String>> entries = hm.entrySet();
for (Map.Entry<String, String> entry : entries) {
System.out.println(entry.getKey()+" "+entry.getValue());
}
hm.forEach((a,b)-> System.out.println(a+" "+b));
HashMap
底层实现:哈希表
保证键唯一
LinkedHashMap
底层实现:链表+哈希表
加上了一个双链表,可以通过双链表找到下一个元素,所以变得存取有序
TreeMap
底层实现:红黑树
可以进行键内部排序
四. Compartor和Comparable
当既有Compartor又有Comparable时比较器的优先级比较高,自然排序比较低
其中第一个元素也会调用compareTo,和自己比较就算为0也会存进去
String自然排序为字典排序
//Compartor是用于集合内部排序使用作用与集合的构造方法 实现的是compare方法
TreeSet<Student> set = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
int ageSort= o1.getAge()-o2.getAge();
//return为负值向左走,为正值向右走
return ageSort==0?o1.getName().compareTo(o2.getName()):ageSort;
}
});
//Compaarable是用于自定义类,实现的是comparTo方法,进行排序
class Student implements Comparable<Student>{
@Override
public int compareTo(Student o) {
//年龄主要排序,姓名次要排序
int agesort= this.getAge()-o.getAge();
return agesort==0?this.getName().compareTo(o.getName()):agesort;
}
五.Collections工具类
可以实现的功能
- 使得集合不可修改
- 使得集合支持同步
- 交换集合中两个值的位置
- 排序
- 反转
- 使其随机
- 添加一个集合
- 二分查找
- 最大,最小
六.数据结构模拟实现
双向链表
节点
//节点
class DouleNode<T> {
T val; // 节点值
DouleNode prev; //直接前驱
DouleNode next; //直接后驱
public DouleNode(T val) {
this.val = val;
}
}
实现
public class DoubleLinkNode<T> { //双向链表
private DouleNode head; //头部
int size; //链表长度
public DoubleLinkNode() { //初始化头部
head = null;
size = 0;
}
//头插法
public void addFromFirst(T data) {
DouleNode newNode = new DouleNode(data);
if (this.head == null) {
this.head = newNode;
size++;
} else {
newNode.next = this.head;
this.head.prev = newNode;
this.head = newNode;
size++;
}
}
//尾插法
public void addLast(T data) {
DouleNode newNode = new DouleNode(data);
if (this.head == null) {
this.head = newNode;
size++;
} else {
DouleNode cache = this.head;
while (cache.next != null) {
cache = cache.next;
}
cache.next = newNode;
newNode.prev = cache;
size++;
}
}
;
//任意位置插入,第一个数据节点为0号下标
public boolean addIndex(int index, T data) {
DouleNode cache = head;
if (size < index) {
System.out.println("插入失败");
return false;
} else {
for (int i = 0; i < index; i++) {
cache = cache.next;
}
DouleNode newNode = new DouleNode(data);
newNode.next = cache;
///
newNode.prev = cache.prev;
cache.prev = newNode;
newNode.prev.next = newNode;
size++;
return true;
}
}
;
//打印
public void display() {
DouleNode cache = head;
for (int i = 0; i < size; i++) {
System.out.println(cache.val);
cache = cache.next;
}
}
;
/* //查找是否包含关键字key是否在链表当中
public boolean contains(T key){
};
//删除第一次出现关键字为key的节点
public void remove(int key);
//删除所有值为key的节点
public void removeAllKey(int key);
//得到双链表的长度
public int size();
//打印双链表
//销毁双链表
public void clear();*/
}
单链表
节点
class Node<T>{ //数据类型 节点
T data; //节点数据
Node next; //指向下一个节点
public Node() {
}
public Node(T data, Node next) {
this.data = data;
this.next = next;
}
@Override
public String toString() {
return "Node{" +
"data=" + data +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Node<?> node = (Node<?>) o;
return Objects.equals(data, node.data) ;
}
@Override
public int hashCode() {
return Objects.hash(data, next);
}
}
实现
public class LinkNode<T> {
Node phead;
public LinkNode() { //初始化
phead = new Node<>(null, null);
}
//头插法
public void addFromHead(T data){
Node<T> newNode = new Node<>(data, null);
Node last= phead;
if (last.next == null) {
last.next=newNode;
return;
}
newNode.next=last.next;
last.next=newNode;
}
//尾插法
public void addFromLast(T data){
Node<T> newNode = new Node<>(data,null);
Node last= phead;
if (last.next == null) {
last.next=newNode;
return;
}
while (last.next != null) {
last.next=last;
}
last.next=newNode;
}
//删除节点
public void remove(T t){
Node<T> remoNode = new Node<>(t, null);
Node last = phead;
if (last == null) {
System.out.println("链表为空");
return;
}
while(last.next!=null){
if (last.next.equals(remoNode)){
last.next=last.next.next;
System.out.println("删除成功");
break;
}
last=last.next;
}
}
//循环打印
public void printLink(){
Node last=phead;
if (last.next == null) {
System.out.println("链表为空");
return;
}
while (last.next != null) {
System.out.println(last.next);
last=last.next;
}
}
}
循环链表的实现
节点
class LoopNode<T> {
T data;
LoopNode next;
public LoopNode() {
}
public T getData() {
return data;
}
public LoopNode(T data) {
this.data = data;
}
public void setData(T data) {
this.data = data;
}
public LoopNode getNext() {
return next;
}
public void setNext(LoopNode next) {
this.next = next;
}
}
实现
public class LoopLinkNode {
private LoopNode head;
private LoopNode last;
int size;
public LoopLinkNode() {
head=last=null;
size=0;
}
public void addFromHead(LoopNode newNode){
if(size==0){
newNode.setNext(newNode);
head=last=newNode;
size++;
}else{
last.setNext(newNode);
newNode.setNext(head);
head=newNode;
size++;
}
}
public void addFromLast(LoopNode newNode){
if(size==0){
newNode.setNext(newNode);
head=last=newNode;
size++;
}else{
last.setNext(newNode);
newNode.setNext(head);
last=newNode;
size++;
}
}
public void printLoopList(){
LoopNode cache = new LoopNode();
cache=head;
while(cache.next!=head){
System.out.println(cache.getData());
cache=cache.next;
}
System.out.println(cache.getData());
}
}