目录
LinkedList底层用双向链表实现的存储。特点:查询效率低,增删效率高,线程不安全。 双向链表也叫双链表,是链表的一种,它的每个数据节点中都有两 个指针,分别指向前一个节点和后一个节点。 所以,从双向链表中 的任意一个节点开始,都可以很方便地找到所有节点。
LinkedList的存储结构图
每个节点都应该有3部分内容:
- class Node<E> {
- Node<E> previous; //前一个节点
- E element; //本节点保存的数据
- Node<E> next; //后一个节点
- }
List实现类的选用规则
如何选用ArrayList、LinkedList、Vector?
1 需要线程安全时,用Vector。
2 不存在线程安全问题时,并且查找较多用ArrayList(一般使用它)
3 不存在线程安全问题时,增加或删除元素较多用LinkedList
LinkedList容器的使用(List标准)
LinkedList实现了List接口,所以LinkedList是具备List的存储特征的 (有序,元素有重复)。
- public class LinkedListTest {
- public static void main(String[] args) {
- //实例化LinkedList容器
- List<String> list = new LinkedList<>();
- //添加元素
- boolean a = list.add("a");
- boolean b = list.add("b");
- boolean c = list.add("c");
- list.add(3,"a");
- System.out.println(a+"\t"+b+"\t"+c);
- for(int i=0;i<list.size();i++){
- System.out.println(list.get(i));
- }
- }
- }
LinkedList容器的使用(非List标准)
- public class LinkedListTest2 {
- public static void main(String[] args) {
- System.out.println("-------LinkedList-------------");
- //将指定元素插入到链表开头
- LinkedList<String> linkedList1 = new LinkedList<>();
- linkedList1.addFirst("a");
- linkedList1.addFirst("b");
- linkedList1.addFirst("c");
- for (String str:linkedList1){
- System.out.println(str);
- }
- System.out.println("----------------------");
- //将指定元素插入到链表结尾
- LinkedList<String> linkedList = new LinkedList<>();
- linkedList.addLast("a");
- linkedList.addLast("b");
- linkedList.addLast("c");
- for (String str:linkedList){
- System.out.println(str);
- }
- System.out.println("---------------------------");
- //返回此链表的第一个元素
-
- System.out.println(linkedList.getFirst());
- //返回此链表的最后一个元素
-
- System.out.println(linkedList.getLast());
- System.out.println("-----------------------");
- //移除此链表中的第一个元素,并返回这个元素
- linkedList.removeFirst();
- //移除此链表中的最后一个元素,并返回这个元素
- linkedList.removeLast();
- for (String str:linkedList){
- System.out.println(str);
- }
- System.out.println("-----------------------");
- linkedList.addLast("c");
- //从此链表所表示的堆栈处弹出一个元素,等效于removeFirst
- linkedList.pop();
- for (String str:linkedList){
- System.out.println(str);
- }
- System.out.println("-------------------");
- //将元素推入此链表所表示的堆栈 这个等效于addFisrt(E e)
- linkedList.push("h");
- for (String str:linkedList){
- System.out.println(str);
- }
- }
- }
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;
- }
- }
成员变量
- transient int size = 0;
-
- /**
- * Pointer to first node.
- * Invariant: (first == null && last == null) ||
- * (first.prev == null && first.item != null)
- */
-
- transient Node<E> first;
-
- /**
- * Pointer to last node.
- * Invariant: (first == null && last == null) ||
- * (last.next == null && last.item != null)
- */
-
- transient Node<E> last;
添加元素
- /**
- * Appends the specified element to the end of this list.
- *
- * <p>This method is equivalent to {@link #addLast}.
- *
- * @param e element to be appended to this list
- * @return {@code true} (as specified by {@link Collection#add})
- */
-
- public boolean add(E e) {
- linkLast(e);
- return true;
- }
-
- /**
- * Links e as last element.
- */
- void linkLast(E e) {
- final Node<E> l = last;
- final Node<E> newNode = new Node<>(l, e, null);
- last = newNode;
- if (l == null)
- first = newNode;
- else
- l.next = newNode;
- size++;
- modCount++;
- }
头尾添加元素
addFirst
- /**
- * Inserts the specified element at the beginning of this list.
- *
- * @param e the element to add
- */
-
- public void addFirst(E e) {
- linkFirst(e);
- }
-
- /**
- * Links e as first element.
- */
- private void linkFirst(E e) {
- final Node<E> f = first;
- final Node<E> newNode = new Node<>(null, e, f);
- first = newNode;
- if (f == null)
- last = newNode;
- else
- f.prev = newNode;
- size++;
- modCount++;
- }
addLast
- /**
- * Appends the specified element to the end of this list.
- *
- * <p>This method is equivalent to {@link #add}.
- *
- * @param e the element to add
- */
-
- public void addLast(E e) {
- linkLast(e);
- }
-
- /**
- * Links e as last element.
- */
- void linkLast(E e) {
- final Node<E> l = last;
- final Node<E> newNode = new Node<>(l, e, null);
- last = newNode;
- if (l == null)
- first = newNode;
- else
- l.next = newNode;
- size++;
- modCount++;
- }
获取元素
- /**
- * Returns the element at the specified position in this list.
- *
- * @param index index of the element to return
- * @return the element at the specified position in this list
- * @throws IndexOutOfBoundsException {@inheritDoc}
- */
-
- public E get(int index) {
- checkElementIndex(index);
- return node(index).item;
- }
- private void checkElementIndex(int index) {
- if (!isElementIndex(index))
- throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
- }
- /**
- * Tells if the argument is the index of an existing element.
- */
-
- private boolean isElementIndex(int index) {
- return index >= 0 && index < size;
- }
- /**
- * Returns the (non-null) Node at the specified element index.
- */
- Node<E> node(int index) {
- // assert isElementIndex(index);
- if (index < (size >> 1)) {
- Node<E> x = first;
- for (int i = 0; i < index; i++)
- x = x.next;
- return x;
- } else {
- Node<E> x = last;
- for (int i = size - 1; i > index; i--)
- x = x.prev;
- return x;
- }
- }
Set接口继承自Collection接口,Set接口中没有新增方法,它和 Collection接口保持完全一致。我们在前面学习List接口的使用方 式,在Set中仍然适用。因此,学习Set的使用将没有任何难度。
Set接口特点
Set特点:无序、不可重复。无序指Set中的元素没有索引,我们只 能遍历查找;不可重复指不允许加入重复的元素。更确切地讲,新 元素如果和Set中某个元素通过equals()方法对比为true,则只能保 留一个。
Set常用的实现类有:HashSet、TreeSet等,我们一般使用 HashSet。
HashSet是Set接口的实现类。是Set存储特征的具体实现。
- public class HashSetTest {
- public static void main(String[] args) {
- //实例化HashSet
- Set<String> set = new HashSet<>();
- //添加元素
- set.add("a");
- set.add("b1");
- set.add("c2");
- set.add("d");
- set.add("a");
- //获取元素,在Set容器中没有索引,所以没有对应的get(int index)方法
- for(String str: set){
- System.out.println(str);
- }
- System.out.println("--------------------");
- //删除元素
- boolean flag = set.remove("c2");
- System.out.println(flag);
- for(String str: set){
- System.out.println(str);
- }
- System.out.println("------------------------");
- int size = set.size();
- System.out.println(size);
- }
- }
HashSet存储特征分析
HashSet 是一个不保证元素的顺序且没有重复元素的集合,是线程 不安全的。HashSet允许有null 元素。
无序:
在HashSet中底层是使用HashMap存储元素的。HashMap底层使 用的是数组与链表实现元素的存储。元素在数组中存放时,并不是 有序存放的也不是随机存放的,而是对元素的哈希值进行运算决定 元素在数组中的位置。
不重复:
当两个元素的哈希值进行运算后得到相同的在数组中的位置时,会 调用元素的equals()方法判断两个元素是否相同。如果元素相同则 不会添加该元素,如果不相同则会使用单向链表保存该元素。
创建Users对象
- public class Users {
- private String username;
- private int userage;
- public Users(String username, int userage) {
-
- this.username = username;
- this.userage = userage;
- }
- public Users() { }
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Users users = (Users) o;
- if (userage != users.userage) return false;
- return username != null ? username.equals(users.username) : users.username == null;
- }
-
- @Override
- public int hashCode() {
- int result = username != null ? username.hashCode() : 0;
- result = 31 * result + userage;
- return result;
- }
- public String getUsername() {
- return username;
- }
- public void setUsername(String username)
- {
- this.username = username;
- }
- public int getUserage() {
- return userage;
- }
- public void setUserage(int userage) {
- this.userage = userage;
- }
- @Override
- public String toString() {
- return "Users{" +
- "username='" + username + '\'' +
- ", userage=" + userage +
- '}';
- }
- }
在HashSet中存储Users对象
- public class HashSetTest2 {
- public static void main(String[] args) {
- //实例化HashSet
- Set<Users> set = new HashSet<>();
- Users u = new Users("oldlu",18);
- Users u1 = new Users("oldlu",18);
- set.add(u);
- set.add(u1);
- System.out.println(u.hashCode());
- System.out.println(u1.hashCode());
- for(Users users:set){
- System.out.println(users);
- }
- }
- }
HashSet底层源码分析
成员变量
- private transient HashMap<E,Object> map;
- // Dummy value to associate with an Object in the backing Map
- private static final Object PRESENT = new Object();
添加元素
- /**
- * Adds the specified element to this set if it is not already present.
- * More formally, adds the specified element <tt>e</tt> to this set if
- * this set contains no element <tt>e2</tt> such that
- * <tt>(e==null ? e2==null : e.equals(e2)) </tt>.
- * If this set already contains the element, the call leaves the set
- * unchanged and returns <tt>false</tt>.
- *
- * @param e element to be added to this set
- * @return <tt>true</tt> if this set did not already contain the specified
- * element
- */
- public boolean add(E e) {
- return map.put(e, PRESENT)==null;
- }
TreeSet实现了Set接口,它是一个可以对元素进行排序的容器。底 层实际是用TreeMap实现的,内部维持了一个简化版的TreeMap, 通过key来存储元素。 TreeSet内部需要对存储的元素进行排序,因 此,我们需要给定排序规则。
排序规则实现方式:
1、通过元素自身实现比较规则。
2、通过比较器指定比较规则。
- public class TreeSetTest {
- public static void main(String[] args) {
- //实例化TreeSet
- Set<String> set = new TreeSet<>();
- //添加元素
- set.add("c");
- set.add("a");
- set.add("d");
- set.add("b");
- set.add("a");
- //获取元素
- for(String str :set){
- System.out.println(str);
- }
- }
- }