资源整合来源与网络以及自身的学习总结,如有侵权请联系我删除~ 有写的不对的地方,请各位在下方评论指出,一起学习进步~
面向对象语言对事物的体现都是以对象的形式,为了方便对多个对象的操作,就要对对象进行存储。另一方面,使用Array存储对象方面具有一些弊端,而Java 集合就像一种容器,可以动态地把多个对象的引用放入容器中
数组在内存存储方面的特点:
数组在存储数据方面的弊端
Java集合类主要由两个根接口Collection和Map派生出来的
Collection派生出了三个子接口:
List
Set
Queue
Map接口派生: Map代表的是存储key-value对的集合,可根据元素的key来访问value。
因此Java集合大致也可分成List、Set、Queue、Map四种接口体系。
Collection接口为集合提供一些统一的访问接口(泛型接口),覆盖了向集合中添加元素、删除元素、以及协助对集合进行遍历访问的相关方法:
方法 | 功能 |
---|---|
boolean add(E e) | 确保此collection包含指定的元素 |
boolean addAll(Collection<?extendsE> c) | 将指定collection中的所有元素都添加到此collection中 |
void clear() | 移除此collection中的所有元素 |
boolean contains(Object o) | 如果此collection包含指定的元素,则返回true |
boolean containsAll(Collection<?> c) | 如果此collection包含指定collection中的所有元素,则返回true |
boolean isEmpty() | 如果此collection不包含元素,则返回true |
Iterator iterator() | 返回在此collection的元素上进行迭代的迭代器(继承自Iterable,是能够使用增强型for(forEach)循环的保证) |
boolean remove(Object o) removeAll(Collection sub) | 从此collection中移除指定元素的单个实例,如果存在的话 |
int size() | 返回此collection中的元素数 |
T[] toArray(T[] a) | 返回包含此collection中所有元素的数组;返回数组的运行时类型与指定数组的运行时类型相同 |
boolean removeIf(Predicate<? super E> filter) | 条件删除 |
结论:集合的contains方法和remove[removeAll]方法中是使用equals方法判断两个对象是否一致的 进一步推论: 集合中凡需要进行对象的比较时,都用对象的equals方法判断
package top.serms.demo21;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Collectors;
/**
* <p>
* </p>
*
* @Author SerMs
* @Email 1839928782@qq.com
* @Date 2022/7/26 10:15
*/
public class Demo3Collection {
public static void main(String[] args) {
//Collection -> Array
Collection<String> c = new HashSet<>(); //HashSet重写了toString所以能直接打印输出
System.out.println(c);
String[] ss = {"aa", "bb", "11", "33"};
System.out.println("ss = " + Arrays.toString(ss));
Collections.addAll(c, ss);
System.out.println("数组转集合: " + c);
System.out.println("遍历一 增强for(forEach循环)");
for (String s : c) {
System.out.print(s + " ");
}
System.out.println();
System.out.println("遍历二 stream流中得forEach");
c.stream().forEach(System.out::printf);
System.out.println();
System.out.println("遍历三 迭代器");
Iterator<String> i = c.iterator();
while (i.hasNext()) {
System.out.print(i.next());
}
System.out.println("遍历四 自带得增强for");
c.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
}
}
输出结果为:
[]
ss = [aa, bb, 11, 33]
数组转集合: [aa, bb, 11, 33]
遍历一 增强for(forEach循环)
aa bb 11 33
遍历二 stream流中得forEach
aabb1133
遍历三 迭代器
aabb1133
遍历四 自带得增强for
aa
bb
11
33
进程已结束,退出代码0
List代表了有序可重复集合,可直接根据元素的索引来访问。
List接口常用的实现类有:ArrayList、LinkedList、Vector。
常用方法 方法 功能 void add(int index, E element) 在列表的指定位置插入指定元素 E get(int index) 返回列表中指定位置的元素 E remove(int index) 移除列表中指定位置的元素 List subList(int fromIndex, int toIndex) 返回列表中指定的fromIndex(包括)和toIndex(不包括)之间的部分视图,返回类型不要进行任何操作 E set(int index, E element) 设置索引index位置的元素 int indexOf(Object o) 从头查找元素,返回索引,没找到返回-1 int lastIndexOf(Object o) 从后面往前面查找 void sort(Comparator<? super E> c) 排序(升序,降序,乱序) 由于列表有序并存在索引,因此除了增强for循环进行遍历外,还可以使用普通的for循环进行遍历 List集合特点
ArrayList是一个动态数组,也是我们最常用的集合,是List类的典型实现。 它允许任何符合规则的元素插入甚至包括null,每一个ArrayList都有一个初始容量(10),该容量代表了数组的大小。 随着容器中的元素不断增加,容器的大小也会随着增加,在每次向容器中增加元素的同时都会进行容量检查,当快溢出时,就会进行扩容操作。 所以如果我们明确所插入元素的多少,最好指定一个初始容量值,避免过多的进行扩容操作而浪费时间、效率。 ArrayList擅长于随机访问,同时ArrayList是非同步的,是一个非线程安全的列表 ArrayList的默认扩容扩展后数组大小为:(原数组长度*3)/2+1 ArrayList的JDK1.8之前与之后的实现区别?
LinkedList:双向链表,内部没有声明数组,而是定义了Node类型的first和last,用于记录首末元素。LinkedList还实现了Deque接口,可以当作双端队列来使用,也就是说,既可以当作“栈”使用,又可以当作队列使用。
同时,定义内部类Node,作为LinkedList中保存数据的基本结构。Node除了保存数据,还定义了两个变量:
prev变量记录前一个元素的位置 next变量记录下一个元素的位置 对于频繁的插入或删除元素的操作,建议使用LinkedList类,效率较高
LinkedList的源码分析: LinkedList list = new LinkedList(); 内部声明了Node类型的first和last属性,默认值为null list.add(123);//将123封装到Node中,创建了Node对象。 其中,Node定义为:体现了LinkedList的双向链表的说法
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
Vector 是一个古老的集合,JDK1.0就有了。大多数操作与ArrayList相同,区别之处在于Vector是线程安全的。 在各种list中,最好把ArrayList作为缺省选择。当插入、删除频繁时,使用LinkedList;Vector总是比ArrayList慢,所以尽量避免使用。
ArrayList 优点: 底层数据结构是数组,查询快,增删慢。 缺点: 线程不安全,效率高 Vector 优点: 底层数据结构是数组,查询快,增删慢。 缺点: 线程安全,效率低 LinkedList 优点: 底层数据结构是链表,查询慢,增删快。 缺点: 线程不安全,效率高
HashSet是Set集合最常用实现类,是其经典实现。
HashSet底层数据结构采用哈希表实现,元素无序且唯一,线程不安全,效率高,可以存储null元素,元素的唯一性是靠所存储元素类型是否重写hashCode()和equals()方法来保证的,如果没有重写这两个方法,则无法保证元素的唯一性。
底层数据结构采用链表和哈希表共同实现,链表保证了元素的顺序与存储顺序一致,哈希表保证了元素的唯一性。
底层数据结构采用二叉树来实现,元素唯一且已经排好序,唯一性同样需要重写hashCode和equals()方法,二叉树结构保证了元素的有序性。
HashSet 底层其实是包装了一个HashMap实现的 底层数据结构是数组+链表 + 红黑树 具有比较好的读取和查找性能, 可以有null 值 通过equals和HashCode来判断两个元素是否相等 非线程安全 HashSet 具有以下特点:
LinkedHashSet 继承HashSet,本质是LinkedHashMap实现 底层数据结构由哈希表(是一个元素为链表的数组)和双向链表组成。 有序的,根据HashCode的值来决定元素的存储位置,同时使用一个链表来维护元素的插入顺序 非线程安全,可以有null 值 LinkedHashSet 是 HashSet 的子类 LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的。 LinkedHashSet插入性能略低于 HashSet,但在迭代访问 Set 里的全部元素时有很好的性能。 LinkedHashSet 不允许集合元素重复。 优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet TreeSet 是一种排序的Set集合,实现了SortedSet接口,底层是用TreeMap实现的,本质上是一个红黑树原理 排序分两种:自然排序(存储元素实现Comparable接口)和定制排序(创建TreeSet时,传递一个自己实现的Comparator对象) 正常情况下不能有null值,可以重写Comparable接口 局可以有null值了。
Map用于保存具有映射关系的数据,Map里保存着两组数据:key和value,它们都可以使任何引用类型的数据,但key不能重复。
Map与Collection并列存在。用于保存具有映射关系的数据:key-value Map 中的 key 和 value 都可以是任何引用类型的数据 Map 中的 key 用Set来存放,不允许重复,即同一个 Map 对象所对应的类,须重写hashCode()和equals()方法 常用String类作为Map的“键” key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到唯一的、确定的 value Map接口的常用实现类:HashMap、TreeMap、LinkedHashMap和Properties。其中,HashMap是 Map 接口使用频率最高的实现类
HashMap在JDK1.7和JDK1.8中的对比
总结:JDK1.8相变化: HashMap map = new HashMap();//默认情况下,先不创建长度为16的数组 当首次调用map.put()时,再创建长度为16的数组 数组为Node类型,在jdk7中称为Entry类型 形成链表结构时,新添加的key-value对在链表的尾部(七上八下) 当数组指定索引位置的链表长度>8时,且map中的数组的长度> 64时,此索引位置上的所有key-value对使用红黑树进行存储。
HashMap、HashTable、TreeMap的区别总结
TreeMap:基于红黑树实现。 HashMap:基于哈希表实现。 HashTable:和 HashMap 类似,但它是线程安全的,这意味着同一时刻多个线程可以同时写入HashTable 并且不会导致数据不一致。它是遗留类,不应该去使用它。现在可以使用 ConcurrentHashMap 来支持线程安全,并且 ConcurrentHashMap 的效率会更高,因为ConcurrentHashMap 引入了分段锁。 LinkedHashMap:使用双向链表来维护元素的顺序,顺序为插入顺序或者最近最少使用(LRU)顺序。
一,强引用 Object obj = new Object(); //只要obj还指向Object对象,Object对象就不会被回收 obj = null; //手动置null 只要强引用存在,垃圾回收器将永远不会回收被引用的对象,哪怕内存不足时,JVM也会直接抛出OutOfMemoryError,不会去回收。如果想中断强引用与对象之间的联系,可以显示的将强引用赋值为null,这样一来,JVM就可以适时的回收对象了 二,软引用 软引用是用来描述一些非必需但仍有用的对象。在内存足够的时候,软引用对象不会被回收,只有在内存不足时,系统则会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常。这种特性常常被用来实现缓存技术,比如网页缓存,图片缓存等。 在 JDK1.2 之后,用java.lang.ref.SoftReference类来表示软引用。 三,弱引用 弱引用的引用强度比软引用要更弱一些,无论内存是否足够,只要 JVM 开始进行垃圾回收,那些被弱引用关联的对象都会被回收。在 JDK1.2 之后,用 java.lang.ref.WeakReference 来表示弱引用。 四,虚引用 虚引用是最弱的一种引用关系,如果一个对象仅持有虚引用,那么它就和没有任何引用一样,它随时可能会被回收,在 JDK1.2 之后,用 PhantomReference 类来表示,通过查看这个类的源码,发现它只有一个构造函数和一个 get() 方法,而且它的 get() 方法仅仅是返回一个null,也就是说将永远无法通过虚引用来获取对象,虚引用必须要和 ReferenceQueue 引用队列一起使用。
(操作数组的工具类:Arrays) Collections 是一个操作 Set、List 和 Map 等集合的工具类 Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法
Collections常用方法 排序操作:(均为static方法) reverse(List):反转 List 中元素的顺序 shuffle(List):对 List 集合元素进行随机排序 sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序 sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序 swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换 查找、替换 Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素 Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素 Object min(Collection) Object min(Collection,Comparator) int frequency(Collection,Object):返回指定集合中指定元素的出现次数 void copy(List dest,List src):将src中的内容复制到dest中 boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换List 对象的所有旧值