一.集合就像一种容器,能够动态的把多个对象的引用html
放入到容器中,存储多个数据 。java
集合数组都是对多个数据进行存储操做的结构,简称java容器面试
2、集合框架、算法
collection接口:单列集合,用来存储一个一个对象api
list接口:存储有序可重复的数据数组
实现类:ArrayList LinkedList 安全
set接口:存储无序,不可重复的数据oracle
实现类:HashSetapp
map接口:双列集合,用来存储一对一对的数据框架
实现类:HashMap
3、collection(为何不用collection? 由于即有序又无序,便可重复,又不可重复,找不到这样的容器,因此才会用List和Set)
Collection coll=new ArrayList(); coll.add("aa");
Collection coll=new ArrayList(); Collection coll1=new ArrayList(); coll1.add("bb"); coll.addAll(coll1);
Collection coll=new ArrayList(); coll.add("aa"); coll.clear();
Collection coll=new ArrayList(); coll.add("aa"); coll.clear(); coll.isEmpty();
例如:
private String name; private int age; public Person() { } public Person(String name, int age) { 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 String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } //重写equals方法,若是不写,则结果为false @Override public boolean equals(Object o) { System.out.println("kaishi l "); if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return age == person.age && Objects.equals(name, person.name); }
import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Date; public class Demo { public static void main(String[] args) { Collection coll=new ArrayList(); coll.add("aa"); coll.add("bb"); System.out.println(coll.contains("aa"));//true ,判断是否有aa,有,则为true coll.add(new String ("huhu")); System.out.println(coll.contains(new String("huhu")));//结果为true,由于string底层已经把equals重写过了,直接判断内容 coll.add(new Person("wwuu",123)); System.out.println(coll.contains(new Person("wwuu",123)));//由于上面已经对Person对象进行重写了equals方法,这里直接判断是否有wuwu,123.有则为true //若是上面没有对Person对象的equals方法进行重写,那么这里为false //总结:contains比较的是内容,等于用equals方法进行比较 //咱们在判断时,会调用obj对象所在类的equals()方法,故而obj所在类要重写equals()方法。 } }
Collection coll=new ArrayList(); Collection coll1=new ArrayList(); coll.add("123"); coll1.add("haha"); coll.addAll(coll1); System.out.println(coll.containsAll(coll1));//true,上面用了addAll()方法,把coll1中的数据都放到了coll中,因此coll包含了coll1,为true coll1.add("uiui"); System.out.println(coll.containsAll(coll1));//false,这里是由于coll1新加了一条数据,而coll中没有这条数据,故而为false
coll.remove(new Person("wwuu",123)); System.out.println(coll);//因为上述的方法中已经对Person这个类进行了equals()方法的重写,故而这里是能够对new Person("wwuu",123)这个元素进行删除的,若是不重写equals方法则删除不了
coll.removeAll(coll1); System.out.println(coll1); System.out.println(coll);
Collection coll2=new ArrayList(); coll2.add(123); coll2.add(new String ("huhu")); coll2.add(new String("bb")); coll.retainAll(coll2); System.out.println(coll);
Collection coll3= Arrays.asList(123,"huhu","bb"); System.out.println(coll2.equals(coll3));
System.out.println(coll3.hashCode());
Object[] objects = coll3.toArray(); for (Object o: objects) { System.out.println(o); }
List list2 = Arrays.asList(new int[]{123, 455}); List list3 = Arrays.asList(new Integer[]{123, 455}); System.out.println(list2.size());//结果是1,判断一个参数 System.out.println(list3.size());//结果是2,判断2个参数
Iterator iterator=coll2.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); }
foreach(Object obj: coll2){ System.out.println(obj); }
他是一个接口,一般用List代替数组,有三个实现类ArrayList、LinkedList、Vector
三者的异同:ArrayList、LinkedList、Vector?
相同点:三个类都实现了List接口;存储数据特色相同,有序,可重复
不一样点:
ArrayList:做为List接口的主要实现类,基本都用他,线程不安全,执行效率高,查询快;底层使用的是Object[ ] elementData数组储存数据
LikedList:对于频繁的插入和删除操做,使用LinkedList比ArrayList效率高,底层使用的是双向链表储存
Vector:List接口的古老实现类,不多用,JDK 1.0就有了,其余的都是1.2出现的,包括List,线程安全,执行效率低;底层是使用Object[ ] elementData储存数据
JDK7:
JDK7在造对象的时候就给把底层的数组进行了初始化
ArrayList arraylist=new ArrayList();
JDK8:并无给原始数组初始化,节省内存空间,这样的方式更好,在咱们调用add方法的时候,咱们的数组才会被建立好
总结:
是一个双向链表,
import java.util.LinkedList; public class LinkedListTest<E> { int size; transient MyNode first; transient MyNode last; private static class MyNode<E>{ private E element; private MyNode prev; private MyNode next; public MyNode(MyNode prev, E ele, MyNode next){ this.element=ele; this.prev=prev; this.next=next; } } //添加元素的方法,里面有一个拼接的方法一个Node分为三部分:【prev】【元素】【next】 public void add(E ele){ appendLast(ele); } //定义l节点为last, public void appendLast(E ele){ MyNode<E> l=last; MyNode newNode=new MyNode(l,ele,null); //last节点为新节点 last=newNode; //判断新节点的【prev】是否为null,是则表示为该新节点为【null】【ele】【null】 //表示新结点为first //不然,表示不是首节点,则把last的【next】指向新结点 if(l==null){ first=newNode; }else { l.next=newNode; } size++; } public void remove(E obj){ //删除元素 //从头开始遍历,一直遍历到结尾,只要没到结尾就继续遍历 for (MyNode pos=first;pos!=null;pos=pos.next){ //判断,当前元素pos是否是想要删除的元素,是的就进行unlink方法,只要找到了就break if (pos.element.equals(obj)){ unlink(pos); break; } } } //这里就是删除元素的方法 private void unlink(MyNode<E> pos){ //首先定义当前pos元素【prev】【ele】【next】 E ele=pos.element; MyNode<E> prev=pos.prev; MyNode<E> next=pos.next; //判断当前元素的【prev】是否是空,若是是空,那就表示是first,为第一个节点 //那就直接把第一个删除,而后把pos后面的节点MyNode<E> next=pos.next 赋值给first //并把当前元素pos的【next】变成null,就不会指向后面的结点了,就断开链接了 if (prev==null){ first=next; pos.next=null; }else { prev.next=next; pos.prev=null; } //判断当前元素是否是最后一个,若是是就删除最后一个元素,即让前一个元素的变成为最后一个last结点 //不然把下一个元素的【prev】指向prev结点 if (next==null){ last=prev; pos.prev=null; }else { next.prev=prev; pos.next=null; } pos.element=null; size--; } }
是接口,存储无序不重复的数据,Set接口继承了Collection接口,同时没有添加新的方法,因此在使用上与List接口的实现类使用方式一致。都是有增长元素的add方法和获取元素个数的size方法
具备三个实现类:
HashSet:做为set接口的主要实现类,线程不安全:能够存储null值
LinkedHashSet:是Hashset的子类,在Hashset基础之上,添加了指针,遍历其内部子类的时候,能够按照添加的顺序遍历。
TreeSet:使用红黑树存储,要求放在TreeSet中的数据必须是同类型,能够按照添加对象的知道属性进行排序
1.无序性:解释HashSet集合如何存放元素:
无序性:不等于随机性。存储的数据在数组中并不是按照数组索引的顺序进行添加。而是根据数据的hash值决定的。
当咱们给HashSet中存放元素的时候,这时并非直接把这个元素就存放到HashSet内部维护的数组中。
而是先根据当前要存放的那个元素,再结合一种算法(这个方法就是Object类中的hashCode方法),算出当前这个元素应该在数组中存储的位置。
在存放数据的时候,若是计算出来的位置上已经有元素,这时还会去调用当前正要存放的这个元素的equals方法,把已经在计算出位置上的那个元素一块儿进行比较,若是equals方法返回的true,就丢弃当前正要存放的元素。若是equals方法返会的false,当前这个对象还要存储。
2.不可重复性:HashSet集合是如何保证元素不重复
不可重复性:表示在hashset中的元素 是不可重复的
当给hashset中存放元素的时候会先调用对象的hashCode方法,计算哈希值,根据哈希值来决定当前对象在集合中的存储位置。
在存储的时候,若是遇到了哈希值相同的元素,这时集合的底层还会去调用当前对象的equals方法,判断当前正要存放的对象和
位置上已经存在的对象是不是同一个对象,equals方法返回的true,就认为相同对象,不保存,若是equals方法返回的false,当前对象已经会被保存
计算哈希值得时候减小冲突
链表与数组的区别:
对于在内存中数据的存放顺序及位置:
数组中元素是按顺序存放的,那么在内存中也是按顺序存放的,可是链表中元素存放的位置与内存中存放的顺序及位置不一致;
2.对于扩容的速度:
链表的速度要快于数组,数据的扩容须要在内存在新申请一片内存空间,而链表直接扩就行
存储双列数据:存储key--value键值对的数据
缘由:在原有的hashMap底层结构基础上,添加一对指针,指向前一个和后一个元素。
对于频繁的遍历操做, 此类执行效率高于HashMap
底层使用红黑树进行排序
1.(高频)HashMap底层实现原理
2.HashMap和Hashtable的异同
Map中 的key:无序的、不可重复,使用set存储全部的key,
要求:key所在的类要重写equals()和hashCode()
Map中的value:无序的、可重复的,使用Collection存储全部的value
要求:value所在的类要重写equals()方法
一个键值对:key-value构成了一个Entry,key、value就是entry的属性
Map中的entry:无序的,不可重复的,使用set存储全部的entry
哈希冲突:
解决方法1:再哈希法===再次计算哈希值,直到每个元素都有惟一的索引(位置)为止
解决方法2:链地址法===使用链表
JDK7
import java.util.HashMap; public class HashMapTest <K,V>{ private Entry<K,V>[]table; private static final Integer INITCAPACITY=8; private int size; public HashMapTest(){ table=new Entry[INITCAPACITY]; } private int size(){ return size; } private V get(Object key){ int hash=key.hashCode(); int i=hash%table.length; for (Entry<K,V> entry=table[i];entry!=null;entry=entry.next){ if (entry.k.equals(key)){ return entry.v; } } return null; } private V put(K key,V value){ int hash=key.hashCode(); int i=hash%table.length; for (Entry<K,V> entry=table[i];entry!=null;entry=entry.next){ if (entry.k.equals(key)){ V oldvalue=entry.v; entry.v=value; return oldvalue; } } addEntry(key, value, i); return null; } private void addEntry(K key, V value, int i) { Entry<K,V> entry=new Entry<K,V>(key,value,table[i]); table[i]=entry; size++; } class Entry<K,V>{ private K k; private V v; private Entry<K,V>next; public Entry() { } public Entry(K k, V v, Entry<K, V> next) { this.k = k; this.v = v; this.next = next; } public K getK() { return k; } public V getV() { return v; } } public static void main(String[] args) { HashMapTest<Integer ,String>hashMapTest=new HashMapTest<Integer, String>(); for(int i=0;i<10;i++){ hashMapTest.put(i,"结束"+i); System.out.println(hashMapTest.get(i)); } } }