精华内容
下载资源
问答
  • ArrayList和Vector在底层都是用数组来存储数据,插入时就会创造一个新数组并把数据全部转移至其内, 因此在这两种数据结构中插入数据会有很大损耗,但是由于数组特性,他们索引取出数据性能较好。 Vector更注重...
     ArrayList和Vector在底层都是用数组来存储数据,插入时就会创造一个新数组并把数据全部转移至其内,
    因此在这两种数据结构中插入数据会有很大损耗,但是由于数组特性,他们索引取出数据性能较好。
    Vector更注重线程安全,所以通用性较差,大多数情况下可以用Arraylist就够了。LinkedList内部结构是双向链表,
    即保存一种含有前后索引的对象的引用的对象的方式(好纠结。。。),所以插入时只需要更改有限几个对象内的引用就可以了,
    所以插入损耗低。但是如果要以索引取出对象就麻烦了。在正着顺序遍历或倒着遍历时LinkedList性能也很好,只是在随机读取(按索引读取)时损耗高
    展开全文
  • LinkedList 底层基于链表,查询速度慢,增删... * 使用LinkedList 存储一副扑克牌 然后实现洗牌功能  *   * 52张  * 一张扑克牌: 花色 点数 梅花6  */ import java.util.LinkedList; import java.util.Ran...

     LinkedList 底层基于链表,查询速度慢,增删速度快。

    package com.itcsat.object;
    /*
     * 使用LinkedList 存储一副扑克牌  然后实现洗牌功能
     * 
     *     52张
     *     一张扑克牌: 花色    点数       梅花6
     */
    
    import java.util.LinkedList;
    import java.util.Random;
    
    class Poker{
        
        String color;//花色
        
        String num; //点数
        
        public Poker(String color, String num) {
            // TODO Auto-generated constructor stub
            this.color = color;
            this.num = num;
        }
        
        @Override
        public String toString() {
            // TODO Auto-generated method stub
            return this.color + this.num;
        }
    }
    
    public class Demo34 {
    
        public static void main(String[] args) {
            // TODO Auto-generated method stub
            
            //生成扑克牌
            LinkedList pokers = createPoker();
            //洗牌
            shufflePoker(pokers);
            //显示
            showPoker(pokers);
    
        }
        
        public static LinkedList createPoker(){
            //该集合用于存储扑克对象
            LinkedList pokers = new LinkedList();
            //生成一副扑克牌
            String[] colors = {"黑桃","红桃","梅花","方块"};
            String[] nums = {"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
            for(int i=0; i<colors.length; i++){
                for(int j=0; j<nums.length; j++){
                    pokers.add(new Poker(colors[i], nums[j]));
                }
            }
            return pokers;
        }
        
        //洗牌功能
        public static void shufflePoker(LinkedList pokers){
            //创建随机数对象
            Random random = new Random();
            for(int i=0; i<100; i++){
                //随机产生两个索引值
                int index1 = random.nextInt(pokers.size());
                int index2 = random.nextInt(pokers.size());
                //根据索引值取出两张牌 然后交换两张牌的顺序
                Poker poker1 = (Poker) pokers.get(index1);
                Poker poker2 = (Poker) pokers.get(index2);
                pokers.set(index1, poker2);
                pokers.set(index2, poker1);
            }
        }
        
        //显示扑克牌
        public static void showPoker(LinkedList pokers){
            for(int i=0; i<pokers.size(); i++){
                System.out.print(pokers.get(i) + ";");
                if((i+1)%13==0){
                    System.out.println();
                }
            }
        }
    }
    

     

    展开全文
  • * 需求:使用LinkedList存储一副扑克牌,然后实现洗牌功能 */ import java.util.LinkedList; import java.util.Random; class Poker{ String color; String num; public Poker(String color, String num
    package com.zyf.day16;
    /**
     * 需求:使用LinkedList存储一副扑克牌,然后实现洗牌功能
     */
    import java.util.LinkedList;
    import java.util.Random;
    
    class Poker{
    	String color;
    	String num;
    	public Poker(String color, String num) {
    		super();
    		this.color = color;
    		this.num = num;
    	}
    	
    	@Override
    	public String toString() {
    		return "{" + color + "," + num + "}";
    	}
    }
    public class demo1 {
        public static void main(String[] args){
        	//该集合用户存储扑克对象
            LinkedList pokers = createPoker();
            //System.out.println(pokers);   
            shufflePoker(pokers);
            showPoker(pokers);
        }
        
        //洗牌功能
        public static void shufflePoker(LinkedList pokers){
        	//创建随机数对象
        	Random random = new Random();
        	for(int i=0;i<100;i++){
        		//随机产生两个索引值
            	int index1 = random.nextInt(pokers.size());
            	int index2 = random.nextInt(pokers.size());
            	//根据索引值取出两张牌,然后交换两张牌的顺序
            	Poker poker1 = (Poker)pokers.get(index1);
            	Poker poker2 = (Poker) pokers.get(index2);
            	pokers.set(index1, poker2);
            	pokers.set(index2, poker1);
        	}
        	
        	
        }
        
        //生成扑克牌的方法
        public static LinkedList createPoker(){
        	LinkedList list = new LinkedList();
        	String[] colors = {"黑桃","红桃","梅花","方片"};
        	String[] nums = {"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
        	for(int i = 0; i < nums.length;i++){
        		for(int j =0 ; j<colors.length;j++){
        			list.add(new Poker(colors[j],nums[i]));
        		}
        	}
        	return list;
        }
        
        public static void showPoker(LinkedList pokers){
        	for(int i = 0;i < pokers.size();i++){
        		System.out.print(pokers.get(i));
        		//换行
        		if(i%10==9){
            		System.out.println();
        		}
    
        	}
        }
    }
    

    展开全文
  • LinkedList

    2017-07-06 10:25:42
    初识LinkedList 上一篇中讲解了ArrayList,本篇文章讲解一下LinkedList的实现。 LinkedList是基于链表...链表原先是C/C++的概念,是一种线性的存储结构,意思是将要存储的数据存在一个存储单元里面,这个存储

    转载自:http://www.cnblogs.com/xrq730/p/5005347.html


    初识LinkedList

    上一篇中讲解了ArrayList,本篇文章讲解一下LinkedList的实现。

    LinkedList是基于链表实现的,所以先讲解一下什么是链表。链表原先是C/C++的概念,是一种线性的存储结构,意思是将要存储的数据存在一个存储单元里面,这个存储单元里面除了存放有待存储的数据以外,还存储有其下一个存储单元的地址(下一个存储单元的地址是必要的,有些存储结构还存放有其前一个存储单元的地址),每次查找数据的时候,通过某个存储单元中的下一个存储单元的地址寻找其后面的那个存储单元。

    这么讲可能有点抽象,先提一句,LinkedList是一种双向链表,双向链表我认为有两点含义:

    1、链表中任意一个存储单元都可以通过向前或者向后寻址的方式获取到其前一个存储单元和其后一个存储单元

    2、链表的尾节点的后一个节点是链表的头结点,链表的头结点的前一个节点是链表的尾节点

    LinkedList既然是一种双向链表,必然有一个存储单元,看一下LinkedList的基本存储单元,它是LinkedList中的一个内部类:

    private static class Entry<E> {
    E element;
    Entry<E> next;
    Entry<E> previous;
    ...
    }

    看到LinkedList的Entry中的"E element",就是它真正存储的数据。"Entry<E> next"和"Entry<E> previous"表示的就是这个存储单元的前一个存储单元的引用地址和后一个存储单元的引用地址。用图表示就是:

     

    四个关注点在LinkedList上的答案

    关  注  点 结      论
    LinkedList是否允许空 允许
    LinkedList是否允许重复数据 允许
    LinkedList是否有序 有序
    LinkedList是否线程安全 非线程安全

     

    添加元素

    首先看下LinkedList添加一个元素是怎么做的,假如我有一段代码:

    1 public static void main(String[] args)
    2 {
    3     List<String> list = new LinkedList<String>();
    4     list.add("111");
    5     list.add("222");
    6 }

    逐行分析main函数中的三行代码是如何执行的,首先是第3行,看一下LinkedList的源码:

    复制代码
     1 public class LinkedList<E>
     2     extends AbstractSequentialList<E>
     3     implements List<E>, Deque<E>, Cloneable, java.io.Serializable
     4 {
     5     private transient Entry<E> header = new Entry<E>(null, null, null);
     6     private transient int size = 0;
     7 
     8     /**
     9      * Constructs an empty list.
    10      */
    11     public LinkedList() {
    12         header.next = header.previous = header;
    13     }
    14     ...
    15 }
    复制代码

    看到,new了一个Entry出来名为header,Entry里面的previous、element、next都为null,执行构造函数的时候,将previous和next的值都设置为header的引用地址,还是用画图的方式表示。32位JDK的字长为4个字节,而目前64位的JDK一般采用的也是4字长,所以就以4个字长为单位。header引用地址的字长就是4个字节,假设是0x00000000,那么执行完"List<String> list = new LinkedList<String>()"之后可以这么表示:

    接着看第4行add一个字符串"111"做了什么:

    1 public boolean add(E e) {
    2 addBefore(e, header);
    3     return true;
    4 }
    复制代码
    1 private Entry<E> addBefore(E e, Entry<E> entry) {
    2 Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
    3 newEntry.previous.next = newEntry;
    4 newEntry.next.previous = newEntry;
    5 size++;
    6 modCount++;
    7 return newEntry;
    8 }
    复制代码

    第2行new了一个Entry出来,可能不太好理解,根据Entry的构造函数,我把这句话"翻译"一下,可能就好理解了:

    1、newEntry.element = e;

    2、newEntry.next = header.next;

    3、newEntry.previous = header.previous;

    header.next和header.previous上图中已经看到了,都是0x00000000,那么假设new出来的这个Entry的地址是0x00000001,继续画图表示:

    一共五步,每一步的操作步骤都用数字表示出来了:

    1、新的entry的element赋值为111;

    2、新的entry的next是header的next,header的next是0x00000000,所以新的entry的next即0x00000000;

    3、新的entry的previous是header的previous,header的previous是0x00000000,所以新的entry的next即0x00000000;

    4、"newEntry.previous.next = newEntry",首先是newEntry的previous,由于newEntry的previous为0x00000000,所以newEntry.previous表示的是header,header的next为newEntry,即header的next为0x00000001;

    5、"newEntry.next.previous = newEntry",和4一样,把header的previous设置为0x00000001;

    为什么要这么做?还记得双向链表的两个特点吗,一是任意节点都可以向前和向后寻址,二是整个链表头的previous表示的是链表的尾Entry,链表尾的next表示的是链表的头Entry。现在链表头就是0x00000000这个Entry,链表尾就是0x00000001,可以自己看图观察、思考一下是否符合这两个条件。

    最后看一下add了一个字符串"222"做了什么,假设新new出来的Entry的地址是0x00000002,画图表示:

    还是执行的那5步,图中每一步都标注出来了,只要想清楚previous、next各自表示的是哪个节点就不会出问题了。

    至此,往一个LinkedList里面添加一个字符串"111"和一个字符串"222"就完成了。从这张图中应该理解双向链表比较容易:

    1、中间的那个Entry,previous的值为0x00000000,即header;next的值为0x00000002,即tail,这就是任意一个Entry既可以向前查找Entry,也可以向后查找Entry

    2、头Entry的previous的值为0x00000002,即tail,这就是双向链表中头Entry的previous指向的是尾Entry

    3、尾Entry的next的值为0x00000000,即header,这就是双向链表中尾Entry的next指向的是头Entry

     

    查看元素

    看一下LinkedList的代码是怎么写的:

    public E get(int index) {
        return entry(index).element;
    }
    复制代码
     1 private Entry<E> entry(int index) {
     2     if (index < 0 || index >= size)
     3         throw new IndexOutOfBoundsException("Index: "+index+
     4                                             ", Size: "+size);
     5     Entry<E> e = header;
     6     if (index < (size >> 1)) {
     7         for (int i = 0; i <= index; i++)
     8             e = e.next;
     9     } else {
    10         for (int i = size; i > index; i--)
    11             e = e.previous;
    12     }
    13     return e;
    14 }
    复制代码

    这段代码就体现出了双向链表的好处了。双向链表增加了一点点的空间消耗(每个Entry里面还要维护它的前置Entry的引用),同时也增加了一定的编程复杂度,却大大提升了效率。

    由于LinkedList是双向链表,所以LinkedList既可以向前查找,也可以向后查找,第6行~第12行的作用就是:当index小于数组大小的一半的时候(size >> 1表示size / 2,使用移位运算提升代码运行效率),向后查找;否则,向前查找

    这样,在我的数据结构里面有10000个元素,刚巧查找的又是第10000个元素的时候,就不需要从头遍历10000次了,向后遍历即可,一次就能找到我要的元素。

     

    删除元素

    看完了添加元素,我们看一下如何删除一个元素。和ArrayList一样,LinkedList支持按元素删除和按下标删除,前者会删除从头开始匹配的第一个元素。用按下标删除举个例子好了,比方说有这么一段代码:

    复制代码
    1 public static void main(String[] args)
    2 {
    3     List<String> list = new LinkedList<String>();
    4     list.add("111");
    5     list.add("222");
    6     list.remove(0);
    7 }
    复制代码

    也就是我想删除"111"这个元素。看一下第6行是如何执行的:

    1 public E remove(int index) {
    2     return remove(entry(index));
    3 }
    复制代码
     1 private E remove(Entry<E> e) {
     2 if (e == header)
     3     throw new NoSuchElementException();
     4 
     5         E result = e.element;
     6 e.previous.next = e.next;
     7 e.next.previous = e.previous;
     8        e.next = e.previous = null;
     9        e.element = null;
    10 size--;
    11 modCount++;
    12        return result;
    13 }
    复制代码

    当然,首先是找到元素在哪里,这和get是一样的。接着,用画图的方式来说明比较简单:

    比较简单,只要找对引用地址就好了,每一步的操作也都详细标注在图上了。

    这里我提一点,第3步、第4步、第5步将待删除的Entry的previous、element、next都设置为了null,这三步的作用是让虚拟机可以回收这个Entry

    但是,这个问题我稍微扩展深入一点:按照Java虚拟机HotSpot采用的垃圾回收检测算法----根节点搜索算法来说,即使previous、element、next不设置为null也是可以回收这个Entry的,因为此时这个Entry已经没有任何地方会指向它了,tail的previous与header的next都已经变掉了,所以这块Entry会被当做"垃圾"对待。之所以还要将previous、element、next设置为null,我认为可能是为了兼容另外一种垃圾回收检测算法----引用计数法,这种垃圾回收检测算法,只要对象之间存在相互引用,那么这块内存就不会被当作"垃圾"对待。

     

    插入元素

    插入元素就不细讲了,看一下删除元素的源代码:

    public void add(int index, E element) {
        addBefore(element, (index==size ? header : entry(index)));
    }
    复制代码
    private Entry<E> addBefore(E e, Entry<E> entry) {
    Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
    newEntry.previous.next = newEntry;
    newEntry.next.previous = newEntry;
    size++;
    modCount++;
    return newEntry;
    }
    复制代码

    如果朋友们理解了前面的内容,我认为这两个方法对你来说,应该是很容易看懂的。

     

    LinkedList和ArrayList的对比

    老生常谈的问题了,这里我尝试以自己的理解尽量说清楚这个问题,顺便在这里就把LinkedList的优缺点也给讲了。

    1、顺序插入速度ArrayList会比较快,因为ArrayList是基于数组实现的,数组是事先new好的,只要往指定位置塞一个数据就好了;LinkedList则不同,每次顺序插入的时候LinkedList将new一个对象出来,如果对象比较大,那么new的时间势必会长一点,再加上一些引用赋值的操作,所以顺序插入LinkedList必然慢于ArrayList

    2、基于上一点,因为LinkedList里面不仅维护了待插入的元素,还维护了Entry的前置Entry和后继Entry,如果一个LinkedList中的Entry非常多,那么LinkedList将比ArrayList更耗费一些内存

    3、数据遍历的速度,看最后一部分,这里就不细讲了,结论是:使用各自遍历效率最高的方式,ArrayList的遍历效率会比LinkedList的遍历效率高一些

    4、有些说法认为LinkedList做插入和删除更快,这种说法其实是不准确的:

    (1)LinkedList做插入、删除的时候,慢在寻址,快在只需要改变前后Entry的引用地址

    (2)ArrayList做插入、删除的时候,慢在数组元素的批量copy,快在寻址

    所以,如果待插入、删除的元素是在数据结构的前半段尤其是非常靠前的位置的时候,LinkedList的效率将大大快过ArrayList,因为ArrayList将批量copy大量的元素;越往后,对于LinkedList来说,因为它是双向链表,所以在第2个元素后面插入一个数据和在倒数第2个元素后面插入一个元素在效率上基本没有差别,但是ArrayList由于要批量copy的元素越来越少,操作速度必然追上乃至超过LinkedList

    从这个分析看出,如果你十分确定你插入、删除的元素是在前半段,那么就使用LinkedList;如果你十分确定你删除、删除的元素在比较靠后的位置,那么可以考虑使用ArrayList。如果你不能确定你要做的插入、删除是在哪儿呢?那还是建议你使用LinkedList吧,因为一来LinkedList整体插入、删除的执行效率比较稳定,没有ArrayList这种越往后越快的情况;二来插入元素的时候,弄得不好ArrayList就要进行一次扩容,记住,ArrayList底层数组扩容是一个既消耗时间又消耗空间的操作,在我的文章Java代码优化中,第9点有详细的解读。

    最后一点,一切都是纸上谈兵,在选择了List后,有条件的最好可以做一些性能测试,比如在你的代码上下文记录List操作的时间消耗

     

    对LinkedList以及ArrayList的迭代

    在我的Java代码优化一文中,第19点,专门提到过,ArrayList使用最普通的for循环遍历,LinkedList使用foreach循环比较快,看一下两个List的定义:

    public class ArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    public class LinkedList<E>
        extends AbstractSequentialList<E>
        implements List<E>, Deque<E>, Cloneable, java.io.Serializable

    注意到ArrayList是实现了RandomAccess接口而LinkedList则没有实现这个接口,关于RandomAccess这个接口的作用,看一下JDK API上的说法:

    为此,我写一段代码证明一下这一点,注意,虽然上面的例子用的Iterator,但是做foreach循环的时候,编译器默认会使用这个集合的Iterator,具体可参见foreach循环原理。测试代码如下:

    复制代码
    public class TestMain
    {
        private static int SIZE = 111111;
        
        private static void loopList(List<Integer> list)
        {
            long startTime = System.currentTimeMillis();
            for (int i = 0; i < list.size(); i++)
            {
                list.get(i);
            }
            System.out.println(list.getClass().getSimpleName() + "使用普通for循环遍历时间为" + 
                    (System.currentTimeMillis() - startTime) + "ms");
            
            startTime = System.currentTimeMillis();
            for (Integer i : list)
            {
                
            }
            System.out.println(list.getClass().getSimpleName() + "使用foreach循环遍历时间为" + 
                    (System.currentTimeMillis() - startTime) + "ms");
        }
        
        public static void main(String[] args)
        {
            List<Integer> arrayList = new ArrayList<Integer>(SIZE);
            List<Integer> linkedList = new LinkedList<Integer>();
            
            for (int i = 0; i < SIZE; i++)
            {
                arrayList.add(i);
                linkedList.add(i);
            }
            
            loopList(arrayList);
            loopList(linkedList);
            System.out.println();
        }
    }
    展开全文
  • 在内存中,数据有两种存储方式:顺序存储和链式存储,在Java中有也对应了两种封装的实现—&gt;ArrayList和LinkedList ArrayList 数组的特点:长度固定,可以用索引直接找到元素 插入数据:由于ArrayList的底层...
  • 线性表可以理解为区别于非线性的数据结构(如树[二叉树]、图)来说的 常见的链表(单链表、循环链表、双链表)就是线性表。另外,栈或队列就是一...1.顺序存储ArrayList 2.链式存储LinkedList 两种存储的特点比较:
  • 需求: 使用LinkedList存储一副扑克牌,然后实现洗牌功能。 */ //扑克类 class Poker{ String color; //花色 String num; //点数 public Poker(String color, String num) { super(); t...
  • 顺序存储结构 Stack Vector 入栈 出栈 查询 用的也是数组 继承的是Vector,而Vector继承的是abstractList.同时ArrayList和LinkedList都继承自abstractList Stack的方法 都是使用了顺序存储结构 push 在数组中...
  • 线性存储结构-LinkedList

    千次阅读 2016-11-14 16:46:55
    LinkedList内部采用链表的形式构建,是一个双向链表。除了继承List外,还继承了Deque接口,可以当做堆栈结构使用。 private static final class Link { ET data; //数据 Link previous, next; //前节点、后节点 ...
  • import java.util.LinkedList; import java.util....需求: 使用LinkedList存储一副扑克牌,然后实现洗牌功能。 */ //扑克类 class Poker{ String color; //花色 String num; //点数 pu...
  • 在上一篇文章中,我们详细介绍了线性表数据结构的原理以及顺序存储结构,并结合ArrayList源码进行了分析,相关文章大家可以点击这里回看我的博客:线性表数据结构解读(一)顺序存储结构ArrayList  本篇文章,我将...
  • ArrayList 和 LinkedList 是 Java 集合框架中用来存储对象引用列表的两个类。ArrayList 和 LinkedList 都实现 List 接口。首先,让我们了解一下它们最重要的父接口——List。 1、List 接口 列表(list)是元素的有序...
  • 顺序存储:插入和删除的平均耗时(n-1)/2,所以时间复杂度是O(n);而查询操作的平均耗时 1,所以时间复杂度是O(1); 链式存储:插入和删除操作:第一次操作,不知道i的位置,插入和删除操作的平均耗时(n-1)/2,给...
  • 上一篇文章 线性表之顺序存储和ArrayList、Vector实现介绍了线性表的顺序存储和ArrayList的实现细节,这一篇主要介绍线性表链式存储。我们知道线性表的顺序存储需要一块连续的内存空间(数组)来存储元素。 链式...
  • 因为不太了解fastjson...所以写了个自定义的顺序存储 JSONObject jsonObject = new JSONObject(); int orignalCount = getTodayCurrentMlfCount(); int waibuCount = 0; int jizhezhanCount = 0; int xhsCoun...
  • 方法一:维护一张表,存储数据插入的顺序,可以使用vector。但是如果删除数据呢 首先得在vector里面找到那个数据,再删除,而删除又要移动大量数据,性能效率很低 使用list,移动问题可以解决,但是查找数据O的...
  • LinkedList源码详解

    2017-09-23 16:32:32
    链表是我们接触最多的数组结构之一,Java的LinkedList实现了链表这一数据...LinkedList可以存储任何元素,包括null。和ArrayList不同的是,LinkedList是一个”顺序存取“的List,而ArrayList是一个”随机存取“的List。
  • LinkedList 源码分析

    2019-06-25 14:04:31
    简介 LinkedList 是 Java 集合框架中一个常用的集合类,底层采用双向链表结构。...但有得必有失,LinkedList 存储元素的节点需要额外的空间存储前驱和后继的引用。另一方面,LinkedList 在链表头部和尾部...
  • public class LinkedList2<E> extends MyAbstractList<E> { private int size; private Node<E> head; public LinkedList2(){ this.size = 0; head = null; } @Override public void add(E t) { // ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 61,507
精华内容 24,602
关键字:

linkedlist顺序存储