您现在的位置是:首页 > 文章详情

java学习笔记--常用集合和迭代器

日期:2018-05-01点击:286

      终于静下心来写这篇博客来谈谈我对集合的理解了。本来想是早点推的,结果发现还是不愿动手敲这一篇说明。想想当初学集合也算是有点小焦躁,一个toArray函数把我搞死了一天也攻不下来,又是看源码 (native真烦)又是google的,也算是有点成果。接下里进入正题吧

先来谈谈数组 先看下面这个栗子:

int[] cur=new int[10];

发现问题没? 数组被定长度了,如果我们一开始定义了这么一个数组,那么我们后期加入其他数值怎么办,报一个数组越界的bug 就GG。

这个时候集合就出来了 还是来看一下栗子:

 ArrayList arrayList3=new ArrayList(); ArrayList<String> arrayList1=new ArrayList<String>(); ArrayList<Integer> arrayList2=new ArrayList<Integer>(); 

第一个栗子:定义了一个集合 可接受任意数值
第二个栗子:定义了一个数组 接受String类型数值
第二个栗子:定义了一个数组 接受Integer类型数值

我们可以得出这个结论:
集合:集合是存储对象数据的集合容器。

集合比数组的优势: 1. 集合可以存储任意类型的对象数据,数组只能存储同一种数据类型 的数据。 2. 集合的长度是会发生变化的,数组的长度是固定的。 

注意:集合重只能存储对象 不能存储基本类型 意思就是集合泛型不能定义为int

ArrayList<int> arrayList1=new ArrayList<int>(); 编译器会报错 

介绍根接口Collection前 先来看看Collection 和 Collections的区别:
      Collection 和Collections是两个不同的概念。Collection是一个接口,所有的集合类(除Map外)都要继承(实现)该接口。它提供了对集合对象进行基本操作的通用接口方法。Collections是一个包装类,它包含有各种有关集合操作的静态多态方法。(Collections是一个工具类,不能实例化)
先来看一下 关系图:
这里写图片描述
是不是看晕了 那么多类
其实平常用到的也就那么几个类
这里写图片描述

这里写图片描述
接下来我只介绍有序可重复排列的集合(List) 无序不可重复排序的集合(Set)和键值对的集合(Map)
先来看一下原始的根接口Collection的常用方法吧
Collection接口中的方法:

增加 add(E e) 添加成功返回true,添加 失败返回false. addAll(Collection c) 把一个集合 的元素添加到另外一个集合中去。 删除 clear() remove(Object o) removeAll(Collection c) retainAll(Collection c) 查看 size() 判断 isEmpty() contains(Object o) containsAll(Collection<?> c) 迭代 toArray() iterator() 

举个栗子:

Collection c = new ArrayList(); c.add("令计划"); //创建集合 Collection c2 = new ArrayList(); c2.add("xx"); c.addAll(c2); // 把c2的元素的添加到c集合 中去。 c2.clear(); //clear()清空集合中的元素 System.out.println("删除成功吗?"+c.remove("美美"));  // remove 指定集合中的元素删除,删除成功返回true 否则false c.removeAll(c2); //删除c集合中与c2的交集元素。 c.retainAll(c2); //保留c集合与c2的交集元素, //其他的元素一并删除。 */ System.out.println("查看元素个数:"+c.size()); System.out.println("集合的元素:"+ c); 
/* 判断 isEmpty() contains(Object o) containsAll(Collection<?> c) 其实contains方法内部是依赖于equals方法进行比较的。 contains是根据 传入的对象的equals进行比较的 */ 迭代: toArray() /*Collection c = new ArrayList(); c.add("令计划"); c.add("徐才厚"); c.add("周永康"); Object[] arr = c.toArray(); //把集合中的元素全部 存储到一个Object的数组中返回。 System.out.println("数组的元素:"+Arrays.toString(arr));*/ 返回的是Object类型的数组 如果要转换成指定的类型 就很麻烦 Object[] arr = list.toArray(); for (int i = 0;i < arr.length; i++) { String e = (String) arr[i]; System.out.println(e); } 那么就是用第二种方法: String[] array =new String[list.size()]; list.toArray(array); 数组的定义不能用基本类型,必须要用包装类型,如int就报错。 int[] i = new int[set.size()]; //报错  Integer[] i =new Integer[set.size()]; //正确  读取里面的元素有两种方法: Integer[] arr=new Integer[al.size]; for (int i =0; i < al.size(); i++) arr[i] = al.get(i); for (Integer x : arr)//内部是迭代器来实现的 System.out.print(x + " ");

总结:
1. 上面中是根接口Collection的基本方法 当然也是适用于List等实现接口中
2. 上面中的根接口存放的是Object类型 也可以指定数据类型采用泛型

 如:Collection<String> collection=new ArrayList<String>(); 
  1. 集合中泛型类型不可以用基本类型 而是要用包装类 (往期文章中有介绍包装类的可以看看)
  2. 集合中toArray以正确的顺序返回一个包含此列表中所有元素的数组 Object类型

不带参数的toArray方法:
数组不能强制转换 不带参数的toArray方法。来看一下这种情况

 ArrayList<Integer> arrayList2=new ArrayList<Integer>(); arrayList2.add(1); Object[] qw=(Integer[])arrayList2.toArray();

会报错 因为toArray()返回的是Object类型数组 记住是进行数据拷贝 数组然后再返回 注意:这个数组是纯Object 而不是由其他类型转换过来的Object类型
再看看这个栗子:

String[] xx=new String[2]; xx[0]="1"; xx[1]="2"; Object[] aa=xx; String[] zz=(String[])aa;

这个栗子是可以的 因为aa是由String[]类型转换过来的 当然可以再强转回去 还有注意的是:(String[])aa; 中只能String[]不能(String)aa; 因为aa是数组类型

带参数的toArray方法
带参数的toArray方法,是根据参数数组的类型,构造了一个对应类型的,长度跟ArrayList的size一致的空数组,虽然方法本身还是以 Object数组的形式返回结果,不过由于构造数组使用的ComponentType跟需要转型的ComponentType一致,就不会产生转型异常。
栗子:

Collection<String> collection=new ArrayList<String>(); collection.add("sdf"); collection.add("qwe"); collection.add("111"); collection.add("222"); String[] aStrings=new String[1]; aStrings=collection.toArray(aStrings);
  1. 如果astring数组小了就会返回一个新的对应泛型数组 赋值给aStrings
  2. 如果刚好或者大了就会把值传入参数中的astrings 多余的部分为null 而不是返回一个新的数组
    注意:toArray(泛型)主要依靠的是arraycopy 但是它是native的
    arraycopy是本地方法 native无法直接用java代码访问

Arraylist用get访问快 但是线程非安全

ArrayList:线程不安全,速度快 数组形式
Vector: 线程安全的,速度慢 数组形式(不多做介绍了 基本被淘汰了)
LinkedList 链表形式 插入删除快(没用过)

迭代器是集合里面很重要的一部分 先来介绍一下迭代器吧
Iterator是一个迭代器接口,专门用来迭代各种Collection集合,包括Set集合和List集合。
Iterator接口包含以下三个方法:
1.boolean hasNext():如果被迭代的集合元素还没有被遍历,则返回true。
2.Object next():返回集合里下一个元素
3.void remove():移除集合里上一次next()返回的元素
栗子:

 Collection xCollection=new ArrayList(); xCollection.add("aa"); xCollection.add("bb"); xCollection.add("cc"); Iterator iterator=xCollection.iterator(); iterator.next(); iterator.remove(); while(iterator.hasNext()) { iterator.next(); iterator.remove(); } System.out.println(xCollection);
listIterator() ListIterator继承了 Iterator 添加: hasPrevious() 判断是否存在上一个元素。 previous() 当前指针先向上移动一个单位,然后再取出当前指针指向的元素。 next(); 先取出当前指针指向的元素,然后指针向下移动一个单位。 add(E e) 把当前有元素插入到当前指针指向的位置上。 每次add的时候add方法内部已经指针向后移动一位了 set(E e) 替换迭代器最后一次返回的元素。 

比较:
Java的Iterator只能够单向移动,从而这个Iterator只能够用来做以下的事情:

1)使用iterator()方法要求容器返回一个Iterator。Iterator将准备好返回序列的第一个元素。
2)使用next()获取序列的下一个元素。
3)使用hasNext()来检测序列中是否还有元素。
4)使用remove()将迭代器新近返回的元素删除。

——————ListItarator—————
ListIterator是一个更加强大的Iterator的子类型,它只能用于List类的访问。
尽管Iterator只能够向前移动,但是ListItarator可以双向移动。
可用的方法:
1)使用hasNext()来检测序列中是否还有元素。
2)使用next()获取序列的下一个元素。
3)使用hasPrevious()来检测序列中是否有上一个元素。
4)使用previous()获取序列的上一个元素。
5)set()方法替换它访问过的最后一个元素。
6)listIterator()方法产生一个指向List开始处的ListIterator。
7)listIterator(n)方法产生一个指向列表索引为n的元素处的ListIterator。
直接看大佬总结的吧 很完善
https://blog.csdn.net/longshengguoji/article/details/41551491
https://blog.csdn.net/StubbornAccepted/article/details/54562154
总结:
使用范围不同,Iterator可以应用于所有的集合,Set、List和Map和这些集合的子类型。而ListIterator只能用于List及其子类型。
重点:
迭代器在变量元素的时候要注意事项: 在迭代器迭代元素 的过程中,不允许使用集合对象改变集合中的元素 个数,如果需要添加或者删除只能使用迭代器的方法进行操作。

如果使用过了集合对象改变集合中元素个数那么就会出现ConcurrentModificationException异常。

迭代元素 的过程中: 迭代器创建到使用结束的时间。

意思就是说迭代器创建到迭代器的使用中间不能出现集合控制集合个数的操作
要操作只能用迭代器方法 操作的结果会显示在集合中
add会跳过添加的下一位
注意:
NoSuchElementException 没有元素的异常。
出现的原因: 没有元素可以被迭代了。。。

集合三大体系之List:

背景:

有序: 集合的有序不是指自然顺序,而是指添加进去的顺序与元素出来的顺序是一致的。
只有List接口下面的集合类才具备索引值。其他接口下面的集合类都没有索引值。 但是集合不能直接集合[i]进行调用

List接口中特有方法: 添加 add(E element)添加到末尾 addAll(Collection<? extends E> c)添加到末尾 add(int index, E element) 把元素添加到集合中的指定索引值位置上。 addAll(int index, Collection<? extends E> c) 把list2的元素添加到list集合指定索引值的位置上。 获取: get(int index) 根据索引值获取集合中的元素 indexOf(Object o) 元素第一次出现在集合中 的索引值 lastIndexOf(Object o) 指定的元素最后一次出现在集合中的索引值 subList(int fromIndex, int toIndex) 指定开始与结束的索引值截取集合中的元素。 修改: set(int index, E element) 使用指定的元素替换指定索引值位置的元素。 迭代 listIterator() List接口特有的迭代器

注意:
可以直接输出list集合 数据出来顺序和add加入的顺序是一样的
get只有list以及子类可以使用

for (int i = 0; i < list.size() ; i++) { System.out.print(list.get(i)+","); }
截取子集 List subList = list.subList(1, 3); //指定开始与结束的索引值截取集合中的元素。 System.out.println("子集合的元素是:"+ subList);

ArrayList:

ArrayList 特有的方法: ensureCapacity(int minCapaci上ty) 指定初始容量 但是一般都是构造函数的时候初始容量 trimToSize() 删除不用的内容 比如十个里面只用到了3个 删除后面7个 但不常用 笔试题目: 使用ArrayList无参的构造函数创建一个 对象时, 默认的容量是多少? 如果长度不够使用时又自增增长多少? ArrayList底层是维护了一个Object数组实现 的,使用无参构造函数时,Object数组默认的容量是10,当长度不够时,自动增长0.5倍。 使用三种方式遍历集合的元素. List xx=new ArrayList(); xx.add("aa"); xx.add("bb"); xx.add("cc"); xx.add(1,"dd");  //第一种get for(int i=0;i<4;i++) { System.out.print(xx.get(i)+" "); } System.out.print("\n"); //第二种迭代器 ListIterator listIterator=xx.listIterator(); while(listIterator.hasNext()) { System.out.print(listIterator.next()+" "); } System.out.print("\n"); // System.out.print(listIterator.next()+" "); //报错 因为超出范围了 //第三种逆序迭代 while(listIterator.hasPrevious()) { System.out.print(listIterator.previous()+" "); } System.out.print("\n"); // for(Object zz: xx) { System.out.print(zz+" "); }
 // 需求: 编写一个函数清除集合中重复元素。 如果书号是一样就视为重复元素。 要求: 遍历集合元素的时候必须使用迭代器。 get 迭代器 public static ArrayList clearRepeat(ArrayList list){ //创建一个新的集合 ArrayList newList = new ArrayList(); //获取迭代器 Iterator it = list.iterator(); while(it.hasNext()){ Book book = (Book) it.next(); //从旧集合中获取的元素 if(!newList.contains(book)){ //如果新集合没有包含该书籍,那么就存储到新集合中 newList.add(book); } } return newList; }

Linkedlist:

 Linkedlist特有的方法: 1:方法介绍 addFirst(E e) addLast(E e) getFirst() getLast() removeFirst() removeLast() 2:数据结构 1:栈 (1.6) : 主要是用于实现堆栈数据结构的存储方式。 先进后出 push() 左边压入 pop() 左边取出 2:队列(双端队列1.5): 主要是为了让你们可以使用LinkedList模拟队列数据结构的存储方式。 先进先出 offer() 右边压入 poll() 左边取出 3:返回逆序的迭代器对象 descendingIterator() 返回逆序的迭代器对象 

Iterator it = list.descendingIterator();
while(it.hasNext()){
System.out.println(it.next());
}
返回的是逆序的

List总结:

集合 的体系: ------------| Collection 单例集合的根接口 ----------------| List 如果是实现了List接口的集合类,具备的特点: 有序,可重复。 -------------------| ArrayList ArrayList 底层是维护了一个Object数组实现的。 特点: 查询速度快,增删慢。 -------------------| LinkedList LinkedList 底层是使用了链表数据结构实现的, 特点: 查询速度慢,增删快。 -------------------| Vector(了解即可) 底层也是维护了一个Object的数组实现的,实现与ArrayList是一样的,但是Vector是线程安全的,操作效率低。 ----------------| Set 如果是实现了Set接口的集合类,具备的特点: 无序,不可重复。 笔试题: 说出ArrayLsit与Vector的区别? 相同点: ArrayList与Vector底层都是使用了Object数组实现的。 不同点: 1. ArrayList是线程不同步的,操作效率高。 Vector是线程同步的,操作效率低。 2. ArrayList是JDK1.2出现,Vector是jdk1.0的时候出现的。 */ public class Demo1 { public static void main(String[] args) { Vector v = new Vector(); //添加元素 v.addElement("张三"); v.addElement("李四"); v.addElement("王五"); //迭代该集合 Enumeration e = v.elements(); //获取迭代器 while(e.hasMoreElements()){ System.out.println(e.nextElement()); } } }

集合三大体系之Set:
set之HashSet
——–| Set 如果是实现了Set接口的集合类,具备的特点: 无序,不可重复
————-| HashSet 底层是使用了哈希表来支持的,特点: 存取速度快.

hashSet的实现原理: 往Haset添加元素的时候,HashSet会先调用元素的hashCode方法得到元素的哈希值 ,
然后通过元素 的哈希值经过移位等运算,就可以算出该元素在哈希表中 的存储位置。

情况1: 如果算出元素存储的位置目前没有任何元素存储,那么该元素可以直接存储到该位置上。 情况2: 如果算出该元素的存储位置目前已经存在有其他的元素了,那么会调用该元素的equals方法与 该位置的元素再比较一次,如果equals返回的是true,那么该元素与这个位置上的元素就视为重复元素 ,不允许添加,如果equals方法返回的是false,那么该元素运行 添加。 
@Override public int hashCode() { System.out.println("=======hashCode====="); return this.id; } @Override public boolean equals(Object obj) { System.out.println("======equals======"); Person p = (Person)obj; return this.id==p.id; } 重写hascode和equals HashSet set = new HashSet(); set.add(new Person(110,"狗娃")); set.add(new Person(220,"狗剩")); set.add(new Person(330,"铁蛋")); //在现实生活中只要编号一致就为同一个人. System.out.println("添加成功吗?"+set.add(new Person(110,"狗娃"))); System.out.println("集合的元素:"+set);

1.equal()相等的两个对象他们的hashCode()肯定相等,也就是用equal()对比是绝对可靠的。

2.hashCode()相等的两个对象他们的equal()不一定相等,也就是hashCode()不是绝对可靠的。

所有对于需要大量并且快速的对比的话如果都用equal()去做显然效率太低,所以解决方式是,每当需要对比的时候,首先用hashCode()去对比,如果hashCode()不一样,则表示这两个对象肯定不相等(也就是不必再用equal()去再对比了),如果hashCode()相同,此时再对比他们的equal(),如果equal()也相同,则表示这两个对象是真的相同了,这样既能大大提高了效率也保证了对比的绝对正确性!

这种大量的并且快速的对象对比一般使用的hash容器中,比如hashset,hashmap,hashtable等等,比如hashset里要求对象不能重复,则他内部必然要对添加进去的每个对象进行对比,而他的对比规则就是像上面说的那样,先hashCode(),如果hashCode()相同,再用equal()验证,如果hashCode()都不同,则肯定不同,这样对比的效率就很高了。
总结:

1、equals方法用于比较对象的内容是否相等(覆盖以后)

2、hashcode方法只有在集合中用到

3、当覆盖了equals方法时,比较对象是否相等将通过覆盖后的equals方法进行比较(判断对象的内容是否相等)。

4、将对象放入到集合中时,首先判断要放入对象的hashcode值与集合中的任意一个元素的hashcode值是否相等,如果不相等直接将该对象放入集合中。如果hashcode值相等,然后再通过equals方法判断要放入对象与集合中的任意一个对象是否相等,如果equals判断不相等,直接将该元素放入到集合中,否则不放入。
。HashSet,就是通过其中的元素(对象)的hashconde来区分对象是否唯一的。所以,HashSet的对象中着一个集合中对象的hashcode的list,每次执行set.add(obj)的时候,都会取出obj的hashcode与其内部的list进行比较,如果没有与之相等的,就加进set里去,同时把那个obj的hashcode加到list里面去;如果有相等的,就再调用obj的equals方法与各个对象进行比较,如果没有相等的,就加入到set里面去,如果还有相等的,就不执行加入操作。

**总结HashSet : 哈希表是根据hasCode来存放的 如果hasCode一样的话 equals又不一样
那么两个对象都是放在同一个hasCode地址内的
而且interator的输出顺序是无序的**

set之TreeSet
TreeSet()
/*
treeSet添加自定义元素:

treeSet要注意的事项:
1. 往TreeSet添加元素的时候,如果元素本身具备了自然顺序的特性,那么就按照元素自然顺序的特性进行排序存储。
2. 往TreeSet添加元素的时候,如果元素本身不具备自然顺序的特性,那么该元素所属的类必须要实现Comparable接口,把元素
的比较规则定义在compareTo(T o)方法上。

3. 如果比较元素的时候,compareTo方法返回 的是0,那么该元素就被视为重复元素,不允许添加. (注意:TreeSet与HashCode、equals方法是没有任何关系。) 4. 往TreeSet添加元素的时候, 如果元素本身没有具备自然顺序 的特性, 而元素所属的类也没有实现Comparable接口,那么必须要在创建TreeSet的时候传入一个 比较器。 5. 往TreeSet添加元素的时候,如果元素本身不具备自然顺序的特性,而元素所属的类已经实现了 Comparable接口, 在创建TreeSet对象的时候也传入了比较器那么是以比较器的比较规则优先使用。 

如何自定义定义比较器: 自定义一个类实现Comparator接口即可,把元素与元素之间的比较规则定义在compare方法内即可。

 自定义比较器的格式 : class 类名 implements Comparator{ } 推荐使用:使用比较器(Comparator)。 

栗子:

public int compareTo(Object arg0) { Person person=(Person)arg0; return this.number-person.number; } 就是比较number来排序对象 如果0则重复不添加 如果this.number-person.number则为从小到大排序 person.number-this.number则相反 举例: class Person implements Comparable{ int temp; int number; public Person(int temp,int number){ this.temp=temp; this.number=number; } @Override public String toString() { // TODO Auto-generated method stub return this.temp+" "+this.number; } @Override public int compareTo(Object arg0) { // TODO Auto-generated method stub Person person=(Person)arg0; return this.number-person.number; } } class MyComparator implements Comparator{ @Override public int compare(Object arg0, Object arg1) { Person person1=(Person)arg0; Person person2=(Person)arg1; return person1.temp-person2.temp; 从小到大 } } class new1{ public static void main(String[] args) { MyComparator myComparator=new MyComparator(); Set set=new TreeSet(myComparator); set.add(new Person(1,4)); set.add(new Person(2,3)); set.add(new Person(6,2)); set.add(new Person(4,1)); set.add(new Person(3,0)); System.out.println(set); } public static void ha(Object zz) { } }

总结 如果没有自然顺序就需要自定义添加类继承Comparable接口并且改写compareTo方法
或者定义类继承Comparator接口 然后传入TreeSet

如果两个都有那么就按照Comparetor的来排序

TreeSet是可以对字符串进行排序 的, 因为字符串已经实现了Comparable接口。

字符串的比较规则:

情况一: 对应位置有不同的字符出现, 就比较的就是对应位置不同的字符。 情况 二:对应位置上 的字符都一样,比较的就是字符串的长度。 

/*
需求:将字符串中的数值进行排序。
例如String str=”8 10 15 5 2 7”; —-> “2 5 7 8 10 15”
*/

public class Demo8 { public static void main(String[] args) { String str="8 10 15 5 2 7"; String[] datas = str.split(" "); TreeSet tree = new TreeSet(); for(int i = 0 ; i<datas.length ; i++){ tree.add(Integer.parseInt( datas[i])); // 字符串转int类型数据是需要使用Integer.parseInt() } //遍历treeSet的元素拼接成对应的字符串 Iterator it = tree.iterator(); while(it.hasNext()){ System.out.print(it.next()+" "); } } 

}

实现 如果号码相同就按照名字来排序

public int compareTo(Object arg0) { // TODO Auto-generated method stub Person person=(Person)arg0; if((this.number-person.number)!=0) return this.number-person.number; else { return this.name.compareTo(person.name); } }

集合三大体系之Map:

Map与List、Set接口不同,它是由一系列键值对组成的集合,提供了key到Value的映射。同时它也没有继承Collection。在Map中它保证了key与value之间的一一对应关系。也就是说一个key对应一个value,所以它不能存在相同的key值,当然value值可以相同。实现map的有:HashMap、TreeMap、HashTable、Properties、EnumMap。

HashMap:

创建对象: > Map<String, Integer> map=new HashMap<String,Integer>(); 添加:put 可以相同的key值,但是添加的value值会覆 盖前面的,返回值是前一个,如果没有就返回null 添加一个对象:putAll:如果重复了 添加的那个会覆盖掉前面的 返回值方法 删除内容 remove 传入键 删除该键对应的对象( 删除关联对象,指定key对象) 全部删除 clear(); 判断是否为空: isEmpty(); 断集合中是否包含指定的key boolean containsKey(Object key) 判断集合中是否包含指定的value boolean containsValue(Object value) 返回map的长度 size() get(Object key) 通过指定的key对象获取value对象 如果没有的话 就返回null 遍历Map 迭代的方法迭代: keySet() values() entrySet() 1.map转换成set类型 通过map.keySet 然后遍历Iterator遍历set返回key值通过map的get来得到值 Set<String> set=map.keySet(); Iterator<String> iterator=set.iterator(); while(iterator.hasNext()) { String num=iterator.next(); System.out.println(map.get(num)); 2.// 第二种方式: // 通过values 获取所有值,不能获取到key对象 // Collection<V> values() Collection<String> vs = map.values(); Iterator<String> it = vs.iterator(); while (it.hasNext()) { String value = it.next(); System.out.println(" value=" + value); 3.map对象传入set 第三种方式: Map.Entry public static interface Map.Entry<K,V> 通过Map中的entrySet()方法获取存放Map.Entry<K,V>对象的Set集合。 Set<Map.Entry<K,V>> entrySet() 面向对象的思想将map集合中的键和值映射关系打包为一个对象,就是Map.Entry ,将该对象存入Set集合,Map.Entry是一个对象,那么该对象具备的getKey,getValue获得键和值。 Set<Map.Entry<String, Integer>> set=map.entrySet(); Iterator<Map.Entry<String, Integer>> iterator=set.iterator(); while(iterator.hasNext()) { //返回的是封装了key和value对象的Map.Entry对象 Map.Entry<String, Integer> it=iterator.next(); String aString=it.getKey(); int a=it.getValue(); System.out.println(aString+a);

举栗子:

底层是哈希表数据结构,线程是不同步的,可以存入null键,null值。要保证键的唯一性,需要覆盖hashCode方法,和equals方法。 案例:自定义对象作为Map的键。 class Person{ public String name; public int age; public Person(String name,int age) { this.name=name; this.age=age; } @Override public boolean equals(Object arg0) { // TODO Auto-generated method stub if(arg0 instanceof Person) { Person person=(Person)arg0; return this.name.equals(person.name)&&this.age==person.age; }else { return false; } } @Override public int hashCode() { // TODO Auto-generated method stub return this.name.hashCode()+age*20; } @Override public String toString() { // TODO Auto-generated method stub return this.name+" "+this.age; } } class new1{ public static void main(String[] args) { Map<Person, Integer> map=new HashMap<Person,Integer>(); map.put(new Person("a", 1), 2); map.put(new Person("b", 1), 2); map.put(new Person("b", 1), 2); map.put(new Person("c", 1), 3); map.put(new Person("d", 1), 4); Set<Map.Entry<Person, Integer>> set=map.entrySet(); Iterator<Map.Entry<Person, Integer>> iterator=set.iterator(); while(iterator.hasNext()) { Map.Entry<Person, Integer> aEntry=iterator.next(); Person xPerson=aEntry.getKey(); int a=aEntry.getValue(); System.out.println(xPerson+"|"+a); } System.out.println(map); } }

TreeMap
内部以red-black(红-黑)树数据结构实现

TreeMap的排序,TreeMap可以对集合中的键进行排序。如何实现键的排序? 方式一:元素自身具备比较性 和TreeSet一样原理,需要让存储在键位置的对象实现Comparable接口,重写compareTo方法,也就是让元素自身具备比较性,这种方式叫做元素的自然排序也叫做默认排序。 方式二:容器具备比较性 当元素自身不具备比较性,或者自身具备的比较性不是所需要的。那么此时可以让容器自身具备。需要定义一个类实现接口Comparator,重写compare方法,并将该接口的子类实例对象作为参数传递给TreeMap集合的构造方法。 注意:当Comparable比较方式和Comparator比较方式同时存在时,以Comparator的比较方式为主; 注意:在重写compareTo或者compare方法时,必须要明确比较的主要条件相等时要比较次要条件。(假设姓名和年龄一直的人为相同的人,如果想要对人按照年龄的大小来排序,如果年龄相同的人,需要如何处理?不能直接return 0,以为可能姓名不同(年龄相同姓名不同的人是不同的人)。此时就需要进行次要条件判断(需要判断姓名),只有姓名和年龄同时相等的才可以返回0.) 通过return 0来判断唯一性。 举例: class Person implements Comparable<Person> { public String name; public int age; public Person(String name,int age) { this.name=name; this.age=age; } @Override public int compareTo(Person arg0) { // TODO Auto-generated method stub if((this.age-arg0.age)==0) { return this.age-arg0.age; }else { return this.name.compareTo(arg0.name); } } @Override public String toString() { // TODO Auto-generated method stub return this.name+" "+this.age; } } class MyComparator implements Comparator<Person>{ @Override public int compare(Person arg0, Person arg1) { // TODO Auto-generated method stub return arg1.age-arg0.age; } } class new1{ public static void main(String[] args) { MyComparator xx=new MyComparator(); Map<Person, Integer> map=new TreeMap<Person, Integer>(); map.put(new Person("a", 1), 2); map.put(new Person("b", 2), 2); map.put(new Person("c", 3), 3); map.put(new Person("d", 4), 4); Set<Map.Entry<Person, Integer>> set=map.entrySet(); Iterator<Map.Entry<Person, Integer>> iterator=set.iterator(); while(iterator.hasNext()) { Map.Entry<Person, Integer> aEntry=iterator.next(); Person xPerson=aEntry.getKey(); int a=aEntry.getValue(); System.out.println(xPerson+"|"+a); } System.out.println(map); } }
集合的练习 问题: 定义一个Person数组,将Person数组中的重复对象剔除? 思路: 1. 描述一个Person类 2. 将数组转换为Arrays.asList() List 3. Set addAll( list ) 4. hashCode()且equals() import java.util.Arrays; import java.util.Set; import java.util.List; import java.util.HashSet; // 1. 描述Person类 class Person { public String name; public int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public String toString() { return getClass().getName() + " : name=" + this.name + " age=" + this.age; } // 4. 重写hashCode和equals() public int hashCode() { return this.age; } public boolean equals(Object o) { Person p = null; if (o instanceof Person) p = (Person) o; return this.name.equals(p.name) && (this.age == p.age); } } class Demo2 { public static void main(String[] args) { Person[] ps = new Person[] { new Person("jack", 34), new Person("lucy", 20), new Person("lili", 10), new Person("jack", 34) }; // 遍历数组 System.out.println(Arrays.toString(ps)); // 2. 将自定义对象数组转换为List集合 List<Person> list = Arrays.asList(ps); // 3. 将List转换为Set Set<Person> set = new HashSet<Person>(); set.addAll(list); System.out.println(set); } } 
关于数组操作 4.Collections与Arrays 集合框架中的工具类:特点:该工具类中的方法都是静态的。 Collections:常见方法: 1,对list进行二分查找: 前提该集合一定要有序。 int binarySearch(list,key); 求元素所在的索引 (有自然顺序)常用 //必须根据元素自然顺序对列表进行升级排序 //要求list 集合中的元素都是Comparable 的子类。 int binarySearch(list,key,Comparator); 如果是自定义类求键的索引 要传入比较器 key是键 2,对list集合进行排序。 sort(list); //对list进行排序,其实使用的事list容器中的对象的compareTo方法 sort(list,comaprator); 常用 //按照指定比较器进行排序 3,对集合取最大值或者最小值。 常用 max(Collection) max(Collection,comparator) 不具备自然顺序的 min(Collection) min(Collection,comparator) 4,对list集合进行反转。 reverse(list); 不需要传入比较器 因为没有比较过程 5,对比较方式进行强行逆转。 Comparator reverseOrder(); Comparator reverseOrder(Comparator); 6,对list集合中的元素进行位置的置换。 swap(list,x,y); 7,对list集合进行元素的替换。如果被替换的元素不存在,那么原集合不变。 replaceAll(list,old,new); 8,可以将不同步的集合变成同步的集合。 在方法中加synchronized同步锁来实现的。我们知道synchronized锁的开销较大,在程序中不建议使用。 Set synchronizedSet(Set<T> s) Map synchronizedMap(Map<K,V> m) List synchronizedList(List<T> list) 9. 如果想要将集合变数组: 可以使用Collection 中的toArray 方法。注意:是Collection不是Collections工具类 传入指定的类型数组即可,该数组的长度最好为集合的size。 Arrays:用于对数组操作的工具类 1,二分查找,数组需要有序 binarySearch(int[]) binarySearch(double[]) 2,数组排序 sort(int[]) sort(char[])…… 2,将数组变成字符串。 toString(int[]) 3,复制数组。 copyOf(arr,newLenght); newlenght是新数组的长度 如果arr是int new大了的话 剩余是0 如果arr是Integer new大了的话 剩余是null 4,复制部分数组。 copyOfRange(arr,int from,int to):包头不包尾 5,比较两个数组是否相同。 对应位置的元素是否一致 equals(int[],int[]) Arrays.equals改写了object的equals 6,将数组变成集合。 集合可以通过toarray变数组 数组也可以变集合 List asList(T[]); List<Integer> aIntegers=Arrays.asList(a); 这样可以通过集合的操作来操作数组中元素, 但是不可以使用增删方法,add,remove。因为数组长度是固定的,会出现 UnsupportOperationExcetion。 可以使用的方法:contains,indexOf。。。 如果数组中存入的基本数据类型,那么asList会将数组实体作为集合中的元素。 如果数组中的存入的引用数据类型,那么asList会将组中的元素作为集合中 的元素。 import java.util.ArrayList; import java.util.Collections; import java.util.Arrays; import java.util.List; class Demo1 { public static void main(String[] args) { ArrayList<Integer> list = new ArrayList<Integer>(); list.add(4); list.add(3); list.add(1); list.add(2); list.add(3); // 排序 Collections.sort(list); // 折半查找的前提是排序好的元素 System.out.println( Collections.binarySearch( list , 8 ) ); // 找不到返回-插入点-1 // 反序集合输出 Collections.reverse( list ); System.out.println( list ); // 求最值 System.out.println( Collections.max( list ) ); // 4 // fill() 使用指定的元素替换指定集合中的所有元素 // Collections.fill( list, 5 ); System.out.println( list ); // 将数组转换为集合 Integer is[] = new Integer[]{6,7,8}; List<Integer> list2 = Arrays.asList(is); list.addAll( list2 ); System.out.println( list ); // 将List转换为数组 Object [] ins = list.toArray(); System.out.println( Arrays.toString( ins ) ); } }
总结: list还有map都可以使用get set不行 因为set没有索引 是无序的 先插入的数不一定先输出 比较的话 Hash表需要改写hashCode和equals return 0表示重复 Tree的话需要继承Comparable重写或者定义比较容器 继承Comparator this-传入的值 就是从小到大排序 返回0就是重复添加 比较字符串是否相等用equals 比较字符串大小用compareTo MapSet类似方法 Map 键不能重复 HashMap底层是哈希表数据结构,线程是不同步的,可以存入null键,null值。 HashMap有一个叫做Entry的内部类,它用来存储key-value对。 要保证键的唯一性,需要覆盖hashCode方法,和equals方法。 | LinkedHashMap: 该子类基于哈希表又融入了链表。可以Map集合进行增删提高效率。 TreeMap 底层是二叉树数据结构。可以对map集合中的键进行排序。需要使用Comparable或者Comparator 进行比较排序。return 0,来判断键的唯一性。 数组 数组存储区间是连续的,占用内存严重,故空间复杂的很大。但数组的二分查找时间复杂度小,为O(1);数组的特点是:寻址容易,插入和删除困难; 链表 链表存储区间离散,占用内存比较宽松,故空间复杂度很小,但时间复杂度很大,达O(N)。链表的特点是:寻址困难,插入和删除容易。 哈希表 那么我们能不能综合两者的特性,做出一种寻址容易,插入删除也容易的数据结构?答案是肯定的,这就是我们要提起的哈希表。哈希表((Hash table)既满足了数据的查找方便,同时不占用太多的内容空间,使用也十分方便。

Java集合细节–一:
用数组转list用Arrays.asList
1. 但是这个数组不能用基本类型 而是要用包装类
2. 不要试图改变asList返回的列表,长度不可变
参考:
http://cmsblogs.com/?p=106
http://cmsblogs.com/?cat=5
为集合指定初始容量:
http://cmsblogs.com/?p=1226

原文链接:https://yq.aliyun.com/articles/597395
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章