精华内容
下载资源
问答
  • Map 下面有两个实现类,HashMap 和 TreeMap 一、Map 接口 1、概述 将键映射到值的对象 一个映射不能包含重复的键 每个键最多只能映射到一个值 2、Map接口和Collection接口的不同 Map是双列的,Collection是单列的...

    在这里插入图片描述

    • Map 接口属于抽象类
    • Map 下面有两个实现类,HashMap 和 TreeMap

    一、Map 接口

    1、概述

    • 将键映射到值的对象
    • 一个映射不能包含重复的键
    • 每个键最多只能映射到一个值

    2、Map 集合和 Collection 集合的区别

    • Map集合存储元素是成对出现的,Map集合的键是唯一的,值是可重复的。可以把这个理解为:夫妻对
    • Collection集合存储元素是单独出现的,Collection的儿子Set是唯一的,List是可重复的。可以把这个理解为:光棍(11.11)
    • Map集合的数据结构值针对键有效,跟值无关
    • Collection集合的数据结构是针对元素有效
    package map01;
    
    import java.util.HashMap;
    import java.util.Map;
    import java.util.Set;
    
    public class MapDemo {
        public static void main(String[] args) {
            Map<String, String> m = new HashMap<>();
            m.put("001", "刘德华");
            m.put("001", "张学友");
            m.put("002", "刘亦菲");
            m.put("003", "林志玲");
            m.put("004", "林志玲");
            m.put("001", "刘德华");//完全重复
    
            //增强for循环
            for (Map.Entry<String, String> en : m.entrySet()) {
                System.out.println("键名:" + en.getKey() + "----键值:" + en.getValue());
            }
            System.out.println("------------------------------");
    
            //通过 KeySet 循环
            Set<String> keys = m.keySet();
            for (String k : keys) {
                System.out.println("键名:" + k + "----键值:" + m.get(k));
            }
            System.out.println("+++++++++++++++++++++++++++++++");
            System.out.println(m.containsKey("005"));
            System.out.println(m.containsKey("002"));
            System.out.println(m.containsValue("刘德华"));
            System.out.println(m.containsValue("张学友"));
        }
    }
    

    运行显示:
    在这里插入图片描述
    3、Map 接口的成员方法

    • V put(K key,V value)
    • V remove(Object key)
    • void clear()
    • boolean containsKey(Object key)
    • boolean containsValue(Object value)
    • boolean isEmpty()
    • int size()
    • V get(Object key)
    • Set<K> keySet()
    • Collection<V> values()
    • Set<Map.Entry<K,V>> entrySet()

    4、Map 集合遍历(根据键找值)

    • 获取所有键的集合
    • 遍历键的集合,获取到每一个键
    • 根据键找值

    5、Map 集合遍历(根据键值对对象找键和值)

    • 获取所有键值对对象的集合
    • 遍历键值对对象的集合,获取到每一个键值对对象
    • 根据键值对对象找键和值

    6、Map集合的特点

    • 可以存储键值对的元素

    二、HashMap 实现类

    1、概述

    • 键是哈希表结构,可以保证键的唯一性

    2、HashMap 案例

    • HashMap<String,String>
    • HashMap<Integer,String>
    • HashMap<String,Student>
    • HashMap<Student,String>

    3、案例

    package map02;
    
    public class Student {
        private String name;
        private int age;
    
        public Student() {
            super();
        }
    
        public Student(String name, int age) {
            super();
            this.name = name;
            this.age = age;
        }
    
        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;
        }
    
        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + age;
            result = prime * result + ((name == null) ? 0 : name.hashCode());
            return result;
        }
    
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Student other = (Student) obj;
            if (age != other.age)
                return false;
            if (name == null) {
                if (other.name != null)
                    return false;
            } else if (!name.equals(other.name))
                return false;
            return true;
        }
    }
    
    package map02;
    
    import java.util.HashMap;
    import java.util.Set;
    
    public class HashMapDemo {
        public static void main(String[] args) {
            // 创建集合对象
            HashMap<Student, String> hm = new HashMap<Student, String>();
    
            // 创建学生对象
            Student s1 = new Student("貂蝉", 27);
            Student s2 = new Student("王昭君", 30);
            Student s3 = new Student("西施", 33);
            Student s4 = new Student("杨玉环", 35);
            Student s5 = new Student("貂蝉", 27);
    
            // 添加元素
            hm.put(s1, "8888");
            hm.put(s2, "6666");
            hm.put(s3, "5555");
            hm.put(s4, "7777");
            hm.put(s5, "9999");
    
            // 遍历
            Set<Student> set = hm.keySet();
            for (Student key : set) {
                String value = hm.get(key);
                System.out.println(key.getName() + "---" + key.getAge() + "---" + value);
            }
        }
    }
    

    三、LinkedHashMap 实现类

    1、概述

    • Map 接口的哈希表和链接列表实现,具有可预知的迭代顺序
    • 由哈希表保证键的唯一性
    • 由链表保证键盘的有序(存储和取出的顺序一致)
    package map03;
    
    import java.util.LinkedHashMap;
    import java.util.Set;
    
    public class LinkedHashMapDemo {
        public static void main(String[] args) {
            LinkedHashMap<String, String> lh = new LinkedHashMap<>();
            lh.put("aaa", "java");
            lh.put("bbb", "python");
            lh.put("ccc", "PHP");
            lh.put("ddd", "CSS");
            lh.put("eee", "HTML");
            lh.put("fff", "javascript");
            lh.put("111", "android");
            lh.put("222", "android");
            lh.put("222", "android");
            //遍历
            Set<String> s = lh.keySet();
            for (String key : s) {
                String value = lh.get(key);
                System.out.println("键名:" + key + "---键值:" + value);
            }
        }
    }
    

    运行显示:
    在这里插入图片描述

    四、TreeMap 实现类

    1、概述

    • 键是红黑树结构,可以保证键的排序和唯一性
    • 由链表保证键盘的有序(存储和取出的顺序一致)
    package map04;
    
    import java.util.Set;
    import java.util.TreeMap;
    
    public class TreeMapDemo {
        public static void main(String[] args) {
            TreeMap<String, String> tm = new TreeMap<>();
            tm.put("aaa", "java");
            tm.put("bbb", "python");
            tm.put("ccc", "PHP");
            tm.put("ddd", "CSS");
            tm.put("eee", "HTML");
            tm.put("fff", "javascript");
            tm.put("111", "android");
            tm.put("222", "android");
            tm.put("222", "android");
    
            //遍历
            Set<String> s = tm.keySet();
            for (String key : s) {
                String value = tm.get(key);
                System.out.println("键名:" + key + "---键值:" + value);
            }
        }
    }
    

    运行结果显示:
    在这里插入图片描述

    2、TreeMap 案例

    • TreeMap<String,String>
    • TreeMap<Student,String>

    3、案例

    package map05;
    
    public class Student {
        private String name;
        private int age;
    
        public Student() {
            super();
        }
    
        public Student(String name, int age) {
            super();
            this.name = name;
            this.age = age;
        }
    
        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;
        }
    }
    
    package map05;
    
    import java.util.Comparator;
    import java.util.Set;
    import java.util.TreeMap;
    
    public class TreeMapDemo {
        public static void main(String[] args) {
            TreeMap<Student, String> tm = new TreeMap<Student, String>(new Comparator<Student>() {
                @Override
                public int compare(Student s1, Student s2) {
                    int num1 = s1.getAge() - s2.getAge();
                    int num2 = num1 == 0 ? s1.getName().compareTo(s2.getName()) : num1;
                    return num2;
                }
            });
            Student s1 = new Student("潘安", 18);
            Student s2 = new Student("姜子牙", 19);
            Student s3 = new Student("宋江", 22);
            Student s4 = new Student("李世民", 20);
            Student s5 = new Student("刘备", 21);
            Student s6 = new Student("刘亦菲", 22);
    
            tm.put(s1, "宋朝");
            tm.put(s2, "商朝");
            tm.put(s3, "宋朝");
            tm.put(s4, "唐朝");
            tm.put(s5, "三国");
            tm.put(s6, "现代");
    
            // 遍历
            Set<Student> set = tm.keySet();
            for (Student key : set) {
                String value = tm.get(key);
                System.out.println(key.getName() + "---" + key.getAge() + "---" + value);
            }
        }
    }
    

    五、Hashtable 实现类

    1、Hashtable 和 HashMap 的区别

    • Hashtable:线程安全,效率低。不允许null键和null值
    • HashMap:线程不安全,效率高。允许null键和null值

    2、List,Set,Map等接口是否都继承子Map接口

    • List,Set继承自Collection接口
    • Map接口本身就是一个顶层接口

    3、案例

    package map06;
    
    import java.util.Hashtable;
    
    public class HashtableDemo {
        public static void main(String[] args) {
            Hashtable<String, String> ht = new Hashtable<>();
            ht.put("0001", "Lemon");
            System.out.println(ht);
        }
    }
    
    展开全文
  • 接口和抽象类的区别是什么?...一个类可以实现个接口,但最多只能实现一个抽象类。 一个类实现接口的话要实现接口的所有方法,而抽象类不一定。 接口不能用 new 实例化,但可以声明,但是必须引用一个实现...

    接口和抽象类的区别是什么?

    • 接口的方法默认是 public,所有方法在接口中不能有实现(Java 8 开始接口方法可以有默认实现),而抽象类可以有非抽象的方法。
    • 接口中的实例变量默认是 final 类型的,而抽象类中则不一定。
    • 一个类可以实现多个接口,但最多只能实现一个抽象类。
    • 一个类实现接口的话要实现接口的所有方法,而抽象类不一定。
    • 接口不能用 new 实例化,但可以声明,但是必须引用一个实现该接口的对象。从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为的规范。

    备注:
    在 JDK8 中,接口也可以定义静态方法,可以直接用接口名调用。实现类和实现是不可以调用的。如果同时实现两个接口,接口中定义了一样的默认方法,则必须重写,不然会报错。

    抽象类必须要有抽象方法吗?

    抽象类中不一定包含抽象方法,但是包含抽象方法的类一定要被声明为抽象类。

    抽象类能使用 final 修饰吗?

    抽象类不能用 final 来修饰。当用 final 修饰一个类时,表明这个类不能被继承。 final 类中的所有成员方法都会被隐式地指定为 final 方法,这明显违背了抽象类存在的意义了。

    展开全文
  • 1、一个类可以实现个接口,但却只能继承最多一个抽象类。 2、抽象类可以包含具体方法;接口所有方法都是抽象的。 3、抽象类可以申明和使用字段;接口则不能,但可以创建静态的final常量。 4、抽象类的方法可以...

    接口和抽象类,有什么不同呢?现在,广州达内的老师,将从两个大的方面为您解析接口和抽象类的不同!

    一、接口和抽象类的区别

    1、一个类可以实现多个接口,但却只能继承最多一个抽象类。

    2、抽象类可以包含具体方法;接口所有方法都是抽象的。

    3、抽象类可以申明和使用字段;接口则不能,但可以创建静态的final常量。

    4、抽象类的方法可以是public、protected、private或者默认的package;接口的方法都是public。

    5、抽象类可以定义构造方法;接口不行;

     

    二、接口和抽象类的各自优缺点

    接口缺点:如果向一个java接口加入一个新方法时,所有实现这个接口的类都得编写具体的实现。

    接口优点:一个类可以实现多个接口,接口可以让这个类不仅具有主类型的行为,而且具有其他的次要行为,

    抽象类的缺点:一个类智能由一个超类继承,所以抽象类座位类型定义工具的效能大打折扣

    抽象类的优点:具体类可从抽象类自动得到这些方法的缺省实现。

    以上就是接口和抽象类之间的不同,希望有帮助到您!

    转载于:https://www.cnblogs.com/kawazbolg/p/4113376.html

    展开全文
  • LinkedList实现原理要点概括 ...其中,prev是该节点的上一个节点,next是该节点的下一个节点,item是该节点所包含的值。 4、它的查找是分半查找,先判断index是在链表的哪一半,然后再去对应区域查找,这样最多只要

    LinkedList实现原理要点概括

    一、LinkedList理论知识

    1、LinkedList是List接口的双向链表非同步实现,并允许包括null在内的所有元素。
    2、底层的数据结构是基于双向链表的,该数据结构我们称为节点
    3、双向链表节点对应的类Node的实例,Node中包含成员变量:prev,next,item。其中,prev是该节点的上一个节点,next是该节点的下一个节点,item是该节点所包含的值。
    4、它的查找是分两半查找,先判断index是在链表的哪一半,然后再去对应区域查找,这样最多只要遍历链表的一半节点即可找到
    5、add、remove操作对于LinkedList其运行时间是O(1);get方法的调用为O(N)操作。要是使用一个增强的for循环,对于任意List的运行时间都是O(N),因为迭代器将有效地从一项到下一项推进。

    二、LinkedList数据结构
    在这里插入图片描述
    说明:如上图所示,LinkedList底层使用的双向链表结构,有一个头结点和一个尾结点,双向链表意味着我们可以从头开始正向遍历,或者是从尾开始逆向遍历,并且可以针对头部和尾部进行相应的操作

    三、LinkedList源码分析

    3.1 类的继承关系

    public class LinkedList<E>
        extends AbstractSequentialList<E>
        implements List<E>, Deque<E>, Cloneable, java.io.Serializable
      说明:LinkedList 是一个继承于AbstractSequentialList的双向链表。它也可以被当作堆栈、队列或双端队列进行操作。
             LinkedList 实现 List 接口,能对它进行队列操作。
             LinkedList 实现 Deque 接口,即能将LinkedList当作双端队列使用。
             LinkedList 实现了Cloneable接口,即覆盖了函数clone(),能克隆。
             LinkedList 实现java.io.Serializable接口,这意味着LinkedList支持序列化,能通过序列化去传输。
             LinkedList 是非同步的。
    

    3.2 类的内部类

    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;
        }
    } 
     说明:内部类Node就是实际的结点,用于存放实际元素的地方。
    

    3.3 类的属性

    public class LinkedList<E>
        extends AbstractSequentialList<E>
        implements List<E>, Deque<E>, Cloneable, java.io.Serializable
    {
        // 实际元素个数
        transient int size = 0;
        // 头结点
        transient Node<E> first;
        // 尾结点
        transient Node<E> last;
    }  
    
       说明:LinkedList的属性非常简单,一个头结点、一个尾结点、一个表示链表中实际元素个数的变量。注意,头结点、
         尾结点都有transient关键字修饰,这也意味着在序列化时该域是不会序列化的。  
    

    3.4 类的构造函数

    说明:LinkedList提供了两个构造器,ArrayList比它多提供了一个通过设置初始化容量来初始化类。LinkedList不提供该方法的
    原因:因为LinkedList底层是通过链表实现的,每当有新元素添加进来的时候,都是通过链接新的节点实现的,也就是说它
    的容量是随着元素的个数的变化而动态变化的。而ArrayList底层是通过数组来存储新添加的元素的,所以我们可以为ArrayList设置初始容量
    

    1. LinkedList()型构造函数

    public LinkedList() {
    }
      说明:无参构造函数
    

    2. LinkedList(Collection<? extends E>)型构造函数

     /**
     * 构造包含指定集合元素的列表,按照集合的迭代器返回元素的顺序
     * 传入一个集合作为参数初始化LinkedList。
     *
     * @param  c 将其元素放置在此列表中的集合
     * @throws NullPointerException 如果指定的集合为空
     */
      public LinkedList(Collection<? extends E> c) {
            // 调用无参构造函数
            this();
            // 添加集合中所有的元素
            addAll(c);
        }
    
      说明:会调用无参构造函数,并且会把集合中所有的元素添加到LinkedList中。
    

    3.5 核心函数分析
    1.add函数

    
     public boolean add(E e) {
            // 添加到末尾
            linkLast(e);
            return true;
        }
      说明:add函数用于向LinkedList中添加一个元素,并且添加到链表尾部。具体添加到尾部的逻辑是由linkLast
           函数完成的。
    
    
        void linkLast(E e) {
            // 保存尾结点,l为final类型,不可更改
            final Node<E> l = last;
            // 新生成结点的前驱为l,后继为null
            final Node<E> newNode = new Node<>(l, e, null);
            // 重新赋值尾结点
            last = newNode;    
            if (l == null) // 尾结点为空
                first = newNode; // 赋值头结点
            else // 尾结点不为空
                l.next = newNode; // 尾结点的后继为新生成的结点
            // 大小加1    
            size++;
            // 结构性修改加1
            modCount++;
        }
    }  
    
    1. addAll函数
    /**
     * 通过调用addAll(int index, Collection<? extends E> c)添加 集合
     */
    public boolean addAll(Collection<? extends E> c) {
        return addAll(size, c);
    }
    
    /**
     * checkPositionIndex(index); 检查传入的参数是否合法
     */
    public boolean addAll(int index, Collection<? extends E> c) {
        checkPositionIndex(index);
        // 将集合转换为数组
        Object[] a = c.toArray();
        // numNew 存储数组的长度
        int numNew = a.length;
        // 如果 c:待添加的集合为null,直接返回false,不进行后面的操作
        if (numNew == 0)
            return false;
        // pred:指代 待添加节点的前一个节点
        // succ:指的是待添加节点的位置
        Node<E> pred, succ;
         // 如果index==size,说明此时需要添加LinkedList的集合中每一个元素都是在LinkedList最后面。所以把succ设置为null, pred指向尾结点
         // 否则的话succ指向插入待插入位置的节点。这里用到了node(index)方法,pred指向succ节点的前一个节点
        if (index == size) {
            // 新添加的元素的位置是位于LinkedList最后一个元素的后面,也就是在LinkedList尾部追加元素
            succ = null;
            pred = last;
        } else {
           //寻找index位置存在的元素
            succ = node(index);
            //找到index位置存在的元素的前一个元素
            pred = succ.prev;
        }
    
    
        for (Object o : a) {
            @SuppressWarnings("unchecked") E e = (E) o;
              //将对象包装成一个Node对象
            Node<E> newNode = new Node<>(pred, e, null);
            if (pred == null)
                //如果index位置前一个元素为null,表示当前添加的是第一个元素
                //将当前元素的包装对象设置为第一个
                first = newNode;
            else
            //否则设置index位置前一个元素的下一个元素为当前新添加的元素
                pred.next = newNode;
            //添加完成,需要将pred设置为新添加的元素
            pred = newNode;
        }
    
    	
    	 //如果添加前长度为0,或者添加元素的位置在LinkedList长度之外,那么LinkedList最后元素应该是最后添加的元素
        if (succ == null) {
            last = pred;
        } else {
        // 否者表示在已有元素中间添加元素,那么最后添加的元素的下一个元素应该是添加前index位置对应的元素
            pred.next = succ;
        //添加前index位置的元素,设置前一个元素为最后添加的元素
            succ.prev = pred;
        }
        // 重新设置集合的大小
        size += numNew;
        // 修改的次数-自增。
        modCount++;
        return true;
    }
    
      private void checkPositionIndex(int index) {
            if (!isPositionIndex(index))
                throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
        }
    
       Node<E> node(int index) {
           
        // 当下标位置小于元素数量的一半时,从linkedlist左边开始向后遍历
            if (index < (size >> 1)) {
                Node<E> x = first;
                for (int i = 0; i < index; i++)
                    x = x.next;
                return x;
            } else {
           // 当下标位置大于元素数量的一半时,从linkedlist右边开始向前遍历
                Node<E> x = last;
                for (int i = size - 1; i > index; i--)
                    x = x.prev;
                return x;
            }
        }
    

    3、add有参函数

    		   public void add(int index, E element) {
    		
    		      // 调用checkPositionIndex检查index位置是否合法,当index>=0并且index
    		        checkPositionIndex(index);
    		     // 添加到链表尾部
    		
    		        if (index == size)
    		            linkLast(element);
    		
    		        else
    		     // 添加到链表中间, 调用node方法得到index插入位置的节点
    		            linkBefore(element, node(index));
    		
    		    }
    		
    		     	void linkBefore(E e, Node succ) {
    		
    		      // 先得到插入位置的前驱节点pred
    		         final Node  pred = succ.prev;
    		
    		       // 新建一个节点,前驱节点指向pred,后继节点指向插入位置的节点
    		         final Node  newNode = new Node(pred, e, succ);
    		
    		        // 修改插入位置节点的前驱节点指向新建的节点
    		         succ.prev = newNode;
    		
    		        // 如果新节点是头节点,让first指向新节点
    		         if (pred == null)
    		             first = newNode;
    		        else
    		        // 如果不是,将pred的后继节点指向新建节点
    		            pred.next = newNode;
    		
    		        // 节点个数+1
    		        size++;
    		
    		        // 并发修改次数+1
    		        modCount++;
    		    }
    
    

    4、在链表头部插入一个节点

    		    public void addFirst(E e) {
    		        linkFirst(e);
    		
    		    }
    		    private void linkFirst(E e) {
    		        // 先得到first指向的头节点
    		        final Node f = first;
    		
    		        // 创建一个新节点,前驱节点为null,后继节点指向first
    		        final Node newNode = new Node(null, e, f);
    		
    		        // 让first指向新节点
    		         first = newNode;
    		
    		        // 如果是空链表,last也要指向新节点
    		
    		        if (f == null)
    		            last = newNode;
    		        else
    		       // 如果不是,将头节点的前驱节点指向新节点
    		            f.prev = newNode;
    		
    		        // 节点个数+1
    		        size++;
    		
    		        // 并发修改次数+1
    		        modCount++;
    		    }
    

    5、在链表尾部追加一个节点

    		    public void addLast(E e) {
    		        linkLast(e);
    		
    		    }
    		    void linkLast(E e) {
    		        
    		        // 先得到last指向的尾节点
    		        final Node l = last;
    		
    		        // 创建一个新节点,前驱节点指向last,后继节点为null
    		
    		        final Node newNode = new Node(l, e, null);
    		
    		    // 让last指向新节点
    		        last = newNode;
    		
    		    // 如果是空链表,first也要指向新节点
    		
    		        if (l == null)
    		            first = newNode;
    		        else
    		
    		    // 如果不是,将尾节点的后继节点指向新节点
    		            l.next = newNode;
    		
    		    // 节点个数+1
    		        size++;
    		
    		    // 并发修改次数+1
    		        modCount++;
    		    }
    

    6.get函数

    LinkedList为什么是读效率低,写效率高呢,我们接着往下看:
    
    		public E get(int index) {
    		    checkElementIndex(index);
    		    return node(index).item;
    		}
    		 //判断index是否大于1/2的size,若小于1/size则从first结点开始向后遍历,否则从last节点开始向前遍历
    		  Node<E> node(int index) {
    		    // 当下标位置小于元素数量的一半时,从linkedlist左边开始向后遍历
    		        if (index < (size >> 1)) {
    		            Node<E> x = first;
    		            for (int i = 0; i < index; i++)
    		                x = x.next;
    		            return x;
    		        } else {
    		       // 当下标位置大于元素数量的一半时,从linkedlist右边开始向前遍历
    		            Node<E> x = last;
    		            for (int i = size - 1; i > index; i--)
    		                x = x.prev;
    		            return x;
    		        }
    		}
    
    说明:利用了双向链表的结构特点 (可双向遍历),如果 index 离链表头比较近,就从节点头部遍历。
    否则就从节点尾部开始遍历。使用空间(双向链表的每个节点的前后指针所占用空间)来换取时间。
    
    node() 会以 O(n/2) 的时间复杂度去获取一个结点
    如果索引值大于链表大小的一半,那么将从尾结点开始遍历
    这样的效率是非常低的,特别是当 index 越接近 size 的中间值时。
    
    		private void checkElementIndex(int index) {
    		    if (!isElementIndex(index))
    		        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    		}
    		
    		// 检查元素的有效性,元素的保存下标:0 -> (size-1)
    		// 被get()、set()、remove()等调用
    		private boolean isElementIndex(int index) {
    		    return index >= 0 && index < size;
    		}
    		
    		// 构造异常的具体信息
    		private String outOfBoundsMsg(int index) {
    		    return "Index: "+index+", Size: "+size;
    		}
    

    7、set函数

    			// 将列表中指定位置的元素替换为指定元素
    			public E set(int index, E element) {
    				// 检查下标合法性,0 -> (size-1)
    			    checkElementIndex(index);
    			    Node<E> x = node(index);
    			    // 保存旧元素
    			    E oldVal = x.item;
    				// 替换新元素
    			    x.item = element;
    			    return oldVal;
    			}
    

    8、remove函数

      8.1   删除指定的下标结点
    		public E remove(int index) {
    		    checkElementIndex(index);
    		    return unlink(node(index));
    		}
    		
    
         unlink(Node x)
    		// 取消非空节点x的链接,指定结点的删除
    		// 让删除结点的前一结点与后一结点互相指向
    		E unlink(Node<E> x) {
    		    final E element = x.item;
    		    // 目标结点的后一结点
    		    final Node<E> next = x.next;
    		    // 目标结点的前一结点
    		    final Node<E> prev = x.prev;
    		
    			// 如果前一结点为空,那么后一结点为新的头结点
    		    if (prev == null) {
    		        first = next;
    		    // 不为空,则互相连接
    		    } else {
    		        prev.next = next;
    		        // gc
    		        x.prev = null;
    		    }
    			// 如果后一结点为空,那么前一结点为新的末结点
    		    if (next == null) {
    		        last = prev;
    		    // 不为空,则相互链接
    		    } else {
    		        next.prev = prev;
    		        // gc
    		        x.next = null;
    		    }
    			// gc
    		    x.item = null;
    		    size--;
    		    // 操作数+1
    		    modCount++;
    		    return element;
    		}
    
      8.2 从列表中删除并返回第一个元素。
    	public E removeFirst() {
    	    final Node<E> f = first;
    	    if (f == null)
    	        throw new NoSuchElementException();
    	    return unlinkFirst(f);
    	}
    
    		unlinkFirst(Node f)
    		// 取消非空的第一个节点f的链接
    		private E unlinkFirst(Node<E> f) {
    		    final E element = f.item;
    		    // 取得原头结点的后一结点
    		    final Node<E> next = f.next;
    		    // 取消引用,gc回收
    		    f.item = null;
    		    f.next = null;
    			// 删除了头结点,那第二个结点就是新的头结点
    		    first = next;
    		    // 如果next为null,那么first已经为null,也要将last变为null
    		    if (next == null)
    		        last = null;
    		    // 头结点的前一结点为null
    		    else
    		        next.prev = null;
    		    size--;
    		    // 修改数+1
    		    modCount++;
    		    return element;
    		}
    
    8.3、从列表中移除并返回最后一个元素。
    		public E removeLast() {
    		    final Node<E> l = last;
    		    if (l == null)
    		        throw new NoSuchElementException();
    		    return unlinkLast(l);
    		}
    		
    		// 删除第一个匹配的元素,从开头遍历链表,找寻目标结点
    		public boolean remove(Object o) {
    		    if (o == null) {
    		        for (Node<E> x = first; x != null; x = x.next) {
    		            if (x.item == null) {
    		                unlink(x);
    		                return true;
    		            }
    		        }
    		    } else {
    		        for (Node<E> x = first; x != null; x = x.next) {
    		            if (o.equals(x.item)) {
    		                unlink(x);
    		                return true;
    		            }
    		        }
    		    }
    		    // 未找到,返回false
    		    return false;
    		}
    
    
    	unlinkLast(Node l)
    		
    		// 取消非空最后一个节点l的链接
    		private E unlinkLast(Node<E> l) {
    		    final E element = l.item;
    		    // 取得原末结点的前一结点
    		    final Node<E> prev = l.prev;
    		    // 取消引用,gc回收
    		    l.item = null;
    		    l.prev = null;
    		    // 删除了末结点,那末结点的前一结点变成新的末结点
    		    last = prev;
    		    // 如果prev为null,那么last已经为null,也要将first变为null
    		    if (prev == null)
    		        first = null;
    		    // 末结点的后一结点为null
    		    else
    		        prev.next = null;
    		    size--;
    		    // 修改数+1
    		    modCount++;
    		    return element;
    		}
    

    9、contains(Object o)函数

    // 至少包含一个指定的元素,就返回true
    public boolean contains(Object o) {
        return indexOf(o) != -1;
    }
    
    // 返回该元素第一次出现的下标,从前向后遍历
    public int indexOf(Object o) {
        int index = 0;
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null)
                    return index;
                index++;
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item))
                    return index;
                index++;
            }
        }
        return -1;
    }
    
    // 返回该元素最后一次出现的下标,从后向前遍历
    public int lastIndexOf(Object o) {
        int index = size;
        if (o == null) {
            for (Node<E> x = last; x != null; x = x.prev) {
                index--;
                if (x.item == null)
                    return index;
            }
        } else {
            for (Node<E> x = last; x != null; x = x.prev) {
                index--;
                if (o.equals(x.item))
                    return index;
            }
        }
        return -1;
    }
    

    10、toArray()

    // 通过遍历链表生成数组
    public Object[] toArray() {
        Object[] result = new Object[size];
        int i = 0;
        // 遍历链表,依次加到数组里
        for (Node<E> x = first; x != null; x = x.next)
            result[i++] = x.item;
        return result;
    }
    
    @SuppressWarnings("unchecked")
    public <T> T[] toArray(T[] a) {
    	// 通过反射来保证数组足够容纳链表的所有元素
        if (a.length < size)
            a = (T[])java.lang.reflect.Array.newInstance(
                                a.getClass().getComponentType(), size);
        int i = 0;
        Object[] result = a;
        // 遍历链表,依次加到数组里
        for (Node<E> x = first; x != null; x = x.next)
            result[i++] = x.item;
    
    	// 数组有效元素为:0 -> size-1
        if (a.length > size)
            a[size] = null;
    
        return a;
    }
    

    11、队列、堆栈 LinkedList支持单向、双向队列以及堆栈的操作

    		查找但不删除此列表的头(第一个元素)
    		public E peek() {
    		    final Node<E> f = first;
    		    return (f == null) ? null : f.item;
    		}
    
    		 查找但不删除此列表的头(第一个元素)
    		public E element() {
    		    return getFirst();
    		}
    
    		查找并删除此列表的头(第一个元素)
    		public E poll() {
    		    final Node<E> f = first;
    		    return (f == null) ? null : unlinkFirst(f);
    		}
    
    		查找并删除此列表的头(第一个元素)
    		public E remove() {
    		    return removeFirst();
    		}
    
    		 将指定的元素添加为此列表的末尾(最后一个元素)
    		public boolean offer(E e) {
    		    return add(e);
    		}
    
    		将指定的元素插入此列表的前面
    		public boolean offerFirst(E e) {
    		    addFirst(e);
    		    return true;
    		}
    
    		 在列表末尾插入指定的元素
    		public boolean offerLast(E e) {
    		    addLast(e);
    		    return true;
    		}
    
    		 查找但不删除此列表的头(第一个元素)
    		public E peekFirst() {
    		    final Node<E> f = first;
    		    return (f == null) ? null : f.item;
    		 }
    
    		 查找但不删除此列表的尾(最后一个元素)
    		public E peekLast() {
    		    final Node<E> l = last;
    		    return (l == null) ? null : l.item;
    		}
    
    		 查找并删除此列表的头(第一个元素)
    		public E pollFirst() {
    		    final Node<E> f = first;
    		    return (f == null) ? null : unlinkFirst(f);
    		}
    
    		// 查找并删除此列表的尾(最后一个元素)
    		public E pollLast() {
    		    final Node<E> l = last;
    		    return (l == null) ? null : unlinkLast(l);
    		}
    
    		// 将元素推入此列表所表示的堆栈: 将元素插入到列表的前面
    		public void push(E e) {
    		    addFirst(e);
    		}
    
    		// 从该列表表示的堆栈中弹出一个元素: 删除并返回此列表的第一个元素
    		public E pop() {
    		    return removeFirst();
    		}
    
    		// 删除第一个匹配的元素,从前向后遍历
    		public boolean removeFirstOccurrence(Object o) {
    		    return remove(o);
    		}
    
    		// 删除最后一个匹配的元素,从后向前遍历
    		public boolean removeLastOccurrence(Object o) {
    		    if (o == null) {
    		        for (Node<E> x = last; x != null; x = x.prev) {
    		            if (x.item == null) {
    		                unlink(x);
    		                return true;
    		            }
    		        }
    		    } else {
    		        for (Node<E> x = last; x != null; x = x.prev) {
    		            if (o.equals(x.item)) {
    		                unlink(x);
    		                return true;
    		            }
    		        }
    		    }
    		    return false;
    		}
    

    11、listIterator(int index)

    		// 得到一个从指定下标开始的迭代器
    		public ListIterator<E> listIterator(int index) {
    			// 检查下标的合法性,0 -> size
    		    checkPositionIndex(index);
    		    return new ListItr(index);
    		}
    
    ListItr类 - 迭代器
    
    // ListIterator<E> extends Iterator<E>
    		private class ListItr implements ListIterator<E> {
    			// 上一个返回的元素
    		    private Node<E> lastReturned;
    		    // 下一个返回的元素
    		    private Node<E> next;
    		    // 下一个返回元素的下标
    		    private int nextIndex;
    		    // 在生成ListItr对象时确定的修改数,在遍历迭代器期间
    		    // 若外部类进行了添加、删除等操作,就会改变modCount
    		    // 而expectedModCount没有改变,通过判断两个数是否相同
    		    // 就可以判断集合是否被别的线程修改了,保证线程安全
    		    private int expectedModCount = modCount;
    		
    		    ListItr(int index) {
    		   		// 找到指定下标对应的结点
    		        next = (index == size) ? null : node(index);
    		        // 下一个返回元素的下标
    		        nextIndex = index;
    		    }
    		
    		    public boolean hasNext() {
    		        return nextIndex < size;
    		    }
    		
    		    public E next() {
    		   		// 检查线程安全
    		        checkForComodification();
    		        if (!hasNext())
    		            throw new NoSuchElementException();
    		
    				// 将返回的结点作为lastReturned
    		        lastReturned = next;
    		        // 得到下一结点
    		        next = next.next;
    		        // 下标加1
    		        nextIndex++;
    		        return lastReturned.item;
    		    }
    		    
    		    // 若下标大于0,说明下一个返回的结点有前结点
    		    public boolean hasPrevious() {
    		        return nextIndex > 0;
    		    }
    		
    		    public E previous() {
    		        checkForComodification();
    		        if (!hasPrevious())
    		            throw new NoSuchElementException();
    		
    				// 如果next为null,说明下一个返回的结点是末结点的后一结点null
    				// 此时:lastReturned == next
    		        lastReturned = next = (next == null) ? last : next.prev;
    		        nextIndex--;
    		        return lastReturned.item;
    		    }
    		
    		    public int nextIndex() {
    		        return nextIndex;
    		    }
    		
    		    public int previousIndex() {
    		        return nextIndex - 1;
    		    }
    		
    			// 迭代器中的删除方法,并不会造成线程不安全
    		    public void remove() {
    		   		// 检查线程安全
    		        checkForComodification();
    		        if (lastReturned == null)
    		            throw new IllegalStateException();
    				
    				// lastNext 可能为 null,当lastReturned为末结点时
    		        Node<E> lastNext = lastReturned.next;
    		        // 删除上一个返回的结点,一定不是null,modCount++
    		        unlink(lastReturned);
    		        
    		        // 若调用了previous()方法,则next == lastReturned
    		        if (next == lastReturned)
    		       		// 删除了lastReturned结点,下一个结点为删除结点的下一个结点
    		       		// 下标并未变化,请仔细思考
    		            next = lastNext;
    		        // 若调用了next()方法,则next != lastReturned
    		        else
    		       		// 下标发生了变化,所以要-1
    		            nextIndex--;
    		        lastReturned = null;
    		        // unlink(lastReturned)会增加modCount,这里保持了同步
    		        expectedModCount++;
    		    }
    		
    		    public void set(E e) {
    		        if (lastReturned == null)
    		            throw new IllegalStateException();
    		        checkForComodification();
    		        lastReturned.item = e;
    		    }
    		
    			// 线程安全
    		    public void add(E e) {
    		        checkForComodification();
    		        lastReturned = null;
    		        if (next == null)
    		            linkLast(e);
    		        else
    		            linkBefore(e, next);
    		        // 略过新增的结点,不参与遍历
    		        nextIndex++;
    		        // 同步修改数
    		        expectedModCount++;
    		    }
    		
    			// 遍历剩下的元素
    		    public void forEachRemaining(Consumer<? super E> action) {
    		   		// 判断action是否为null,若是,抛空指针异常
    		        Objects.requireNonNull(action);
    		        while (modCount == expectedModCount && nextIndex < size) {
    		            action.accept(next.item);
    		            lastReturned = next;
    		            next = next.next;
    		            nextIndex++;
    		        }
    		        // 上面的循环可能被打断,需要再次判断线程安全
    		        checkForComodification();
    		    }
    		
    			// 判断修改数是否发生了变化,如果改变了,那么抛出异常
    		    final void checkForComodification() {
    		        if (modCount != expectedModCount)
    		            throw new ConcurrentModificationException();
    		    }
    		}
    

    12、clear

    	public void clear() {
    	    //方便gc回收垃圾
    	    for (Node<E> x = first; x != null; ) {
    	        Node<E> next = x.next;
    	        x.item = null;
    	        x.next = null;
    	        x.prev = null;
    	        x = next;
    	    }
    	    first = last = null;
    	    size = 0;
    	    modCount++;
    	}
    
    展开全文
  • 一个类实现“子接口”:该实现类,需要既实现“子接口”中的方法,又要实现“父接口”中的方法。 Java中的类是单继承的,即一个子类最多只能有一个父类。那么接口呐? Java中,接口是可以继承多个接口的 自然...
  • 集合框架之Map接口

    千次阅读 2015-01-31 11:40:03
    每个键最多只能映射到一个值。 Map 接口提供三种collection视图,允许以键集、值集或键-值映射关系集的形式查看某个映射的内容。映射顺序定义为迭代器在映射的 collection 视图上返回其元素的顺序。某些映射实现可...
  • JVM:大端小端、常量池、字节码文件解析大端小端一个类最多实现65535个接口常量池有三种 大端小端 一个类最多实现65535个接口 占多少个字节 name 什么东西 why u4 magic 魔数 如果不是cafebabe,就不是 u2...
  • 静态代理没有什么好说的,不断的写新的实现与被代理一样的接口,从而来拓展功能。缺点太多,如接口变化,就要重写新的代理。 动态代理使用最多的可能就是传说中的aop了。它解决了静态代理的缺点,即使接口...
  • Java的匿名内部

    万次阅读 多人点赞 2019-06-09 14:42:20
    从上面的定义可以看出,匿名内部必须继承一个父类,或实现个接口,但最多只能继承一个父类,或实现个接口条规则。 匿名内部不能是抽象。 匿名内部不能定义构造器。由于匿名内部没有...
  • 匿名内部适合创建那些只需要使用一次的,它的语法有些...从定义来看,匿名内部必须继承一个父类,或者实现个接口,但是最多只能继承一个父类或者实现个接口。 关于匿名内部,还有如下条规则: 匿名...
  • Java匿名内部和使用

    2020-08-31 17:02:21
    一.匿名内部 匿名内部适合创建那种只需要一次使用的,例如前面...从上面可以看出,匿名内部必须继承一个父类,或者实现个接口,但最多只能继承一个父类,或者实现个接口。 关于匿名内部还有如下条规则
  • 因为API接口存在数据保护情况,一个电影的每一个分类只能抓取前25页,全部评论、好评、中评、差评所有分类能爬100页,每页有20个数据,即最多千条数据。 因为时效性原因,不保证代码能爬到数据,只是给大家一个...
  • Java中的接口

    2017-01-15 14:59:26
    Java的类最多只能从一个父类中继承特性,当然多重继承也有他的优势,他允许一个子类从多个父类中分别继承属性或方法,因此这个子类可以兼容原来多个类的优势,定义子类的时候更具有优势,更加方便,更加高效,Java中...
  • 接口 Map 类型参数: K - 此映射所维护的键的类型 V - 映射值的类型 public interface Map 已知常用实现类:  ...每个键最多只能映射一个值。  通俗的说,键不可以重复,值可以重复(一个值可以被多个键映射
  • 每个键最多映射到一个值 有三种方法看待map接口 4.1 看做key的集合 4.2 看做value的集合 4.3 一组键值对映射 对于Map的实现,像TreeMap要确保其顺序的特征 可变对象不能做key,比如map对象本身不能做为key,但是可以...
  • java_匿名内部

    2016-07-28 15:37:30
    创建匿名内部类时会立即创建一个类的实例,这个类定义立即消失,于是匿名内部类不能重复使用。(有点像是一次性的筷子一样,用完就得扔) 匿名内部类必须继承一个父类,或实现个接口。但是最多继承一个父类或则...
  • 匿名内部类适合创建只需要一次使用的类,创建匿名内部类时会立即创建一个该类的实例,这个类定义立即消失,匿名内部... 匿名内部类必须继承一个父类,或实现个接口,但最多只能继承一个父类,或实现个接口。有
  • 匿名内部类适合创建只需要一次使用的类,创建匿名内部类时会立即创建一个该类的实例,这个类定义立即消失,匿名内部... 匿名内部类必须继承一个父类,或实现个接口,但最多只能继承一个父类,或实现个接口。有
  • Set 集合也实现了 Collection 接口,它主要有两个实现类:HashSet 和 TreeSet。Set 集合中的对象不按特定的方式排序,只是简单地把对象加入集合,集合中不能包含重复的对象,并且最多只允许包含一个 null 元素。
  • 匿名内部

    千次阅读 2011-01-04 16:01:00
     定义匿名内部的格式如下: new 父类构造器(参数列表)|实现接口() { //匿名内部体部分 } 从上面定义可以看出,匿名内部必须继承一个父类,或实现个接口,但最多只能继承一个父类,或实现一个...
  • Java之枚举

    2020-03-09 16:28:00
    在某些情况下,一个类的对象是有限且固定的,比如季节类,它只有4个对象;再比如性别类,它只有两个对象。这种实例有限且固定的类,在Java中被称为枚举类。 枚举类实际上是一种特殊的类,它由关键字enum定义(与...
  • 排序二叉树的实现

    2018-08-11 22:28:04
    结点最多具有两个孩子的树称为二叉树。 相关术语:结点、边、根、孩子、兄弟、叶子、内部结点、祖先、子孙、路径长度、高度。 下面我们来实现一个可排序的二叉树结构。 关键:节点元素大于左孩子节点元素,小于...
  • Set 集合也实现了 Collection 接口,它主要有两个实现类:HashSet 和 TreeSet。Set 集合中的对象不按特定的方式排序,只是简单地把对象加入集合,集合中不能包含重复的对象,并且最多只允许包含一个 null 元素。...
  •  1USB接口简介USB即通用串行总线,可以实现热插拔,采用菊花链结构,最多可以同时连接127台设备,由总线提供电源,并有检错、纠错功能以保证数据正确传输。 USB在PC机上应用时,PC机的操作系统需要支持USB协议,...
  • 1、可实现一个或多个接口,默认集成了java.lang.Enum实现java.lang.Serializable和java.lang.Comparable两个接口),不能显示继承其他父类; 2、非抽象的枚举默认使用final修饰,不能被继承; 3、构造器只能...
  • 委托模式是软件设计模式中的一项基本技巧。在委托模式中,有两个对象参与处理同一个...被委托的接口/类应该满足如下条件:动态委托最多只能委托一个类,但是能够代理多个接口。这个限制来自于Java的单继承模式。一个

空空如也

空空如也

1 2 3 4 5 ... 12
收藏数 229
精华内容 91
关键字:

一个类最多实现两个接口