一、IdentityHashMap
该类特点是: 1.只有全等的key值,该类才会认为两个key值相等。比如new String(“11”) 与new String(“11”),这两个对象就不是全等,而一般的HashMap则认为上面两个对象是相等的。 2.并且该类非常有意思的是,在key-value数据的存储上,类似于HashMap,采用map数组进行存储,但是key-value不是利用链表解决冲突,而是继续计算下一个索引,把数据计算在下一个有效索引的数组中,也就是数据全部存储map数组中,并且table[i]=key 则table[i+1]=value。key-value紧挨着存储在map数组中。 3.内部通过数组存储键值对,相邻元素存在键值对。比如:i 位置是key,i+1位置是value 4.当hashcode相等,出现冲突的时候,通过线性探索发解决冲突问题 5.比较的是引用相等IdentityHashMap与常用的HashMap的区别是: 前者比较key时是“引用相等”而后者是“对象相等”,即对于k1和k2,当k1==k2时,IdentityHashMap认为两个key相等,而HashMap只有在k1.equals(k2) == true 时才会认为两个key相等。 默认的加载因子为2/3,在重新哈希后,加载因子变为1/3.当哈希表中的条目数超出了加载因子与当前容量的乘积时,通过调用 reszie 方法将容量翻倍,重新进行哈希。增加桶数,重新哈希,可能相当昂贵。
先来个测试
//在IdentityHashMap中,是判断key是否为同一个对象,而不是普通HashMap的equals方式判断
@Test
public void testIdentityHashMap() {
String s1 = new String("one");
String s2 = new String("one");
System.out.println( s1.equals(s2)); //true
System.out.println( s1==s2 ); //false
Map map =new IdentityHashMap();
map.put(s1,"first");
map.put(s2,"second");
for (Map.Entry entry: map.entrySet()) {
System.out.println(entry.getKey() +",--" +entry.getValue());
//one,--first
//one,--second
}
System.out.println("idenMap="+map.containsKey("one")); //idenMap=false
System.out.println("idenMap="+map.get("one")); //idenMap=null
System.out.println("idenMap="+map.containsKey(s1)); //idenMap=true
System.out.println("idenMap="+map.get(s1)); //idenMap=first
System.out.println("idenMap="+map.containsKey(s2)); //idenMap=true
System.out.println("idenMap="+map.get(s2)); //idenMap=second
map.clear();
//++++++++++++++++++++++++++++++++++++++++++++++++++++
System.out.println("+++++++++++++++++++++++++++++++++++++++++");
map.put(s1,"first");
map.put(s1,"second");
for (Map.Entry entry: map.entrySet()) {
System.out.println(entry.getKey()+","+entry.getValue()); //one,second
}
System.out.println("idenMap="+map.containsKey(s1)); //idenMap=true
System.out.println("idenMap="+map.get(s1)); //idenMap=second
}
1、构造器
/**
* 没有参数构造器的初始容量。必须是二的幂。预期最大容量为负载因子的2/3,也就是21
*/
private static final int DEFAULT_CAPACITY = 32;
/**
* 采用默认容量的构造器,最大21
*/
public IdentityHashMap() {
init(DEFAULT_CAPACITY);
}
/**
* 设定容量大小的构造器
* 参数期望的最大容量expectedMaxSize不能为负数,否则异常。
* 参数expectedMaxSize并不代表实际的容量大小。
* 通过capacity(expectedMaxSize)方法我们会发现,实际的容量要大一些。
*/
public IdentityHashMap(int expectedMaxSize) {
if (expectedMaxSize < 0)
throw new IllegalArgumentException("expectedMaxSize is negative: "
+ expectedMaxSize);
init(capacity(expectedMaxSize)); //将参数期望的容量大小expectedMaxSize扩大1.5倍再初始化
}
/**
* 将参数期望的容量大小expectedMaxSize扩大1.5倍
* 之后按照比改制大的最小2进制数设定容量大小。
* 返回的result值是实际的容量大小。
*/
private int capacity(int expectedMaxSize) {
int minCapacity = (3 * expectedMaxSize)/2;
int result;
if (minCapacity > MAXIMUM_CAPACITY || minCapacity < 0) {
result = MAXIMUM_CAPACITY;
} else {
result = MINIMUM_CAPACITY;
while (result < minCapacity)
result <<= 1;
}
return result;
}
/**
* 极限容量是initCapacity值的三分之二,而数组长度确实initCapacity的两倍!
* 由此可见IdentityHashMap比HashMap耗费内存空间。
* 等于说,当用到数组长度的三分之一的时候就要进行扩容操作。
* 可见内存空间消耗有多大。
*/
private void init(int initCapacity) {
threshold = (initCapacity * 2)/3;
table = new Object[2 * initCapacity];
}
/**
* @param m the map whose mappings are to be placed into this map
* @throws NullPointerException if the specified map is null
*/
public IdentityHashMap(Map m) {
// Allow for a bit of growth
this((int) ((1 + m.size()) * 1.1));
putAll(m);
}
通过构造器我们知道,IdentityHashMap在初始化的时候就已经构造比较大的map数组以解决可能的冲突问题,以便将数据都存储在数组中。同时为了提高查询效率,极限容量设置的比较小,只有数组长度的三分之一。但是问题也来了,
占用了太大的内存空间。也就是有效利用的空间不足数组总长度的三分之一。
2、方法
1.put方法 该方法最为重要。通过该方法我们知道,IdentityHashMap类在存放key-value对时,不采用链表解决冲突,而是通过nextKeyIndex(i,len)方法找到下一个存放数据的索引值,如果该索引处没有值则存放数据,如果有值,继续nextKeyIndex(i,len)进行查找直到在数组中找到合适的存放位置。同时,如果找到全等的key值,说明已经存放过key,则用新value值替换旧value值。put方法最后,进行resize检查。如果存放下一个数据的长度>=极限容量,则进行扩容。通过put方法,我们可以了解到为什么在初始化table数组的时候,把数组长度定义为设计容量的两倍了。在init()方法中,我们知道,极限容量设计为数组长度的三分之一,说明,当存放的数组达到数组长度的三分之一的时候就要进行扩容。可见IdentityHashMap在内存空间中的消耗有多大。
public V put(K key, V value) {
Object k = maskNull(key);
Object[] tab = table;
int len = tab.length;
int i = hash(k, len);
Object item;
while ( (item = tab[i]) != null) {
if (item == k) {
V oldValue = (V) tab[i + 1];
tab[i + 1] = value;
return oldValue;
}
i = nextKeyIndex(i, len);
}
modCount++;
tab[i] = k;
tab[i + 1] = value;
if (++size >= threshold)
resize(len); // len == 2 * current capacity.
return null;
}
扩容方法:扩展为原来数组长度的两倍。扩容之后进行数据的转移,拷贝到新数组当中。循环内部手动进行了数据的清除,设置旧数组中的无用引用为null.个人理解原因是:数组长度较大,占用内存空间比较大,及时释放内存空间是王道!由于扩容之后空间一定够用,所以直接将原来数组中的数据存放到新数组对应的位置即可。并且数组存放不存在链表,只是数组中,所以管理起来比较方便。
2.resize
扩容方法:扩展为原来数组长度的两倍。扩容之后进行数据的转移,拷贝到新数组当中。循环内部手动进行了数据的清除,设置旧数组中的无用引用为null.个人理解原因是:数组长度较大,占用内存空间比较大,及时释放内存空间是王道!由于扩容之后空间一定够用,所以直接将原来数组中的数据存放到新数组对应的位置即可。并且数组存放不存在链表,只是数组中,所以管理起来比较方便。
private void resize(int newCapacity) {
// assert (newCapacity & -newCapacity) == newCapacity; // power of 2
int newLength = newCapacity * 2;
Object[] oldTable = table;
int oldLength = oldTable.length;
if (oldLength == 2*MAXIMUM_CAPACITY) { // can't expand any further
if (threshold == MAXIMUM_CAPACITY-1)
throw new IllegalStateException("Capacity exhausted.");
threshold = MAXIMUM_CAPACITY-1; // Gigantic map!
return;
}
if (oldLength >= newLength)
return;
Object[] newTable = new Object[newLength];
threshold = newLength / 3;
for (int j = 0; j < oldLength; j += 2) {
Object key = oldTable[j];
if (key != null) {
Object value = oldTable[j+1];
oldTable[j] = null;
oldTable[j+1] = null;
int i = hash(key, newLength);
while (newTable[i] != null)
i = nextKeyIndex(i, newLength);
newTable[i] = key;
newTable[i + 1] = value;
}
}
table = newTable;
}
其他方法
/**
* putAll方法,使用put方法进行数据的复制。
* 没什么说的。原理同上。
*/
public void putAll(Map m) {
int n = m.size();
if (n == 0)
return;
if (n > threshold) // conservatively pre-expand
resize(capacity(n));
for (Entry e : m.entrySet())
put(e.getKey(), e.getValue());
}
/**
* Circularly traverses table of size len.
* 计算下一个key值出现在数组处的索引值
*/
private static int nextKeyIndex(int i, int len) {
return (i + 2 < len ? i + 2 : 0);
}
通过上面的方法我们知道,该类在完成key-value对的存放时,是挨着存放key-value对到数组中。以步进2为间隔进行数据填充。
------------------------------------------------------------------------------------------------------------------------------------
3、查询获取数据方法
/**
* 通过key值获取value对象。
* 通过程序会发现,item==k,说明只有当全等的时候
* 才会返回对象,否则找不到value值返回null。
* 同时,item==k的情况下,tab[i+1]为value值。
* 说明数组i处存放key值,i+1处存放value值。
* 这也解释了初始化数组时候2倍长度的原因了。
* @see #put(Object, Object)
*/
public V get(Object key) {
Object k = maskNull(key);
Object[] tab = table;
int len = tab.length;
int i = hash(k, len);
while (true) {
Object item = tab[i];
if (item == k)
return (V) tab[i + 1];
if (item == null)
return null;
i = nextKeyIndex(i, len);
}
}
/**
* 是否包含指定的key值。与上面的get(key)方法类似
* 原理同上。
*/
public boolean containsKey(Object key) {
Object k = maskNull(key);
Object[] tab = table;
int len = tab.length;
int i = hash(k, len);
while (true) {
Object item = tab[i];
if (item == k)
return true;
if (item == null)
return false;
i = nextKeyIndex(i, len);
}
}
/**
* 是否包含指定的value值。
* 循环中i以2位步长进行循环遍历
* 说明数组偶数处存放value值。
* IdentityHashMap iden =
new IdentityHashMap<>();
iden.put(null, null);
iden.put(null, null);
System.out.println(iden);
System.out.println(iden.containsKey(null));
System.out.println(iden.containsValue(null));
上面的运行结果是:
{null=null}
true
true
* 说明存放的全等的key值会替换原来的value值。
* 存放的key==null的情况下,会通过maskKey(key)方法
* 将key=null的值,替换为NULL_KEY对象。
* 请结合put方法查看代码。
*/
public boolean containsValue(Object value) {
Object[] tab = table;
for (int i = 1; i < tab.length; i += 2)
if (tab[i] == value && tab[i - 1] != null)
return true;
return false;
}
/**
* 是否包含指定的key-value对。
* 原理和上面的containsKey containsValue类似
*/
private boolean containsMapping(Object key, Object value) {
Object k = maskNull(key);
Object[] tab = table;
int len = tab.length;
int i = hash(k, len);
while (true) {
Object item = tab[i];
if (item == k)
return tab[i + 1] == value;
if (item == null)
return false;
i = nextKeyIndex(i, len);
}
}
上面三个是查询map数组中数据的方法,上面的方法依次通过获取key在map数组中的索引进行查询,知道查询到结果为止。
------------------------------------------------------------------------------------------------------------
4、删除数据
/**
* 使用全等的方式比较key值。
* 通过key值,得到hash值,然后得到索引值。
* 如果存在key值,则删除对应位置上的数据。同时size-1.
* 并且使用closeDeletion(i)将数组后面的数据重新remap
* 存放在数组的相应位置当中。
*/
public V remove(Object key) {
Object k = maskNull(key);
Object[] tab = table;
int len = tab.length;
int i = hash(k, len);
while (true) {
Object item = tab[i];
if (item == k) {
modCount++;
size--;
V oldValue = (V) tab[i + 1];
tab[i + 1] = null;
tab[i] = null;
closeDeletion(i);
return oldValue;
}
if (item == null)
return null;
i = nextKeyIndex(i, len);
}
}
/**
* 删除指定的key-value对。
* 原理同上面的remove类似。
* 通过key值得到hash值,然后得到key值在数组中的索引值
* 有了索引值一一比较key值,只有和目标key值==时,才会删除
* 该索引处的数据。
* 删除之后,通过closeDeletion(i)方法将后面的数据重新rehash
* 重新在数组中进行存放。
* 如果没有找到==的key值,则返回false,说明删除不成功。
*/
private boolean removeMapping(Object key, Object value) {
Object k = maskNull(key);
Object[] tab = table;
int len = tab.length;
int i = hash(k, len);
while (true) {
Object item = tab[i];
if (item == k) {
if (tab[i + 1] != value)
return false;
modCount++;
size--;
tab[i] = null;
tab[i + 1] = null;
closeDeletion(i);
return true;
}
if (item == null)
return false;
i = nextKeyIndex(i, len);
}
}
/**
* 该方法是在删除数组中的数据的时候重新rehash数组中的元素
* 重新存放在数组中。
* 关于if语句中的判断有点小麻烦。
* 这个判断需要了解hash值的计算。
* 不过不影响我们大概知道是怎么回事。
* 同时我们也可以知道的是,删除元素并不会使数组的长度减小。
* 这一点比较重要。
*/
private void closeDeletion(int d) {
Object[] tab = table;
int len = tab.length;
Object item;
for (int i = nextKeyIndex(d, len); (item = tab[i]) != null;
i = nextKeyIndex(i, len) ) {
int r = hash(item, len);
if ((i < r && (r <= d || d <= i)) || (r <= d && d <= i)) {
tab[d] = item;
tab[d + 1] = tab[i + 1];
tab[i] = null;
tab[i + 1] = null;
d = i;
}
}
}
由于IdentityHashMap采用在数组中保存key-value数据,并以加长的数组来解决可能引起的冲突,所以数据删除起来比较方便,只不过只有全等的情况下,才会删除key值所对应的value。同时,由于删除一对数据之后导致后面的数据遍历不到,所以当删除一对数据之后,需要对后面的数据重写在map数组上面定位。
--------------------------------------------------------------------------------------------------------------------------------
5 相等方法和hashcode方法
/**
* 比较两个IdentityHashMap对象是否相等
* equals方法可以比较Map类对象,只要entrySet().equals(m.entrySet())返回
* true就可以了。
* 1、如果参数o属于IdentityHashMap类对象,直接使用containsMapping方法逐一
*比较每个key-value对数据是否相等即可。
* 2、如果参数o不属于containsMapping类对象,则使用entrySet()方法得到的
* EntrySet集合进行比较。
* 通过查找该类中的EntrySet内部类,没有equals方法,说明复用父类AbstractSet
* 中的equals方法,通过查找父类的equals方法,我们发现,
* AbstractSet类又调用了AbstractCollection类的
* containsAll(Collection c)方法。经过一系列的调用
* 最终通过比较的是参数o集合的每个数据的(o.equals(it.next()))方法
* 进行判断的。
* 也就是说,如果参数o不是IdentityHashMap类对象,则使用参数o的equals方法
* 进行比较,不要求==全等。
*/
public boolean equals(Object o) {
if (o == this) {
return true;
} else if (o instanceof IdentityHashMap) {
IdentityHashMap m = (IdentityHashMap) o;
if (m.size() != size)
return false;
Object[] tab = m.table;
for (int i = 0; i < tab.length; i+=2) {
Object k = tab[i];
if (k != null && !containsMapping(k, tab[i + 1]))
return false;
}
return true;
} else if (o instanceof Map) {
Map m = (Map)o;
return entrySet().equals(m.entrySet());
} else {
return false; // o is not a Map
}
}
/**
* 计算hash值。
* 最终的hash值与key和value都有关
* 保证每个key-value对的hash值是唯一的。
*/
public int hashCode() {
int result = 0;
Object[] tab = table;
for (int i = 0; i < tab.length; i +=2) {
Object key = tab[i];
if (key != null) {
Object k = unmaskNull(key);
result += System.identityHashCode(k) ^
System.identityHashCode(tab[i + 1]);
}
}
return result;
}
这两个方法是该类的关键所在,hashcode的计算不仅仅和key值有关,而且和value值有关,这样就保证了key-value对具备唯一的hash值。同时通过重写equals方法,判定只有key值全等情况下才会判断key值相等。这就是IdentityHashMap与普通HashMap不同的关键所在。
二、WeakHashMap
1、介绍 WeakHashMap采用弱引用队列关联map数组中存储的数据,该类与普通HashMap类似,解决冲突一样采用链表解决。了解了HashMap再来了解WeakHashMap会很容易上手。之所以采用采用WeakHashMap该类,是因为通过该类可是实现缓存,在内存空间很紧张情况下,使用该类,避免强引用占用大量的内存,销毁掉不用或者过时的对象,较早的释放空间。 该类主要的特点就是使用引用队列,将Entry对象与引用队列关联起来,使得每个Entry对象都是弱引用,先看内部类Entry的定义
private static class Entry extends WeakReference implements Map.Entry {
V value;
int hash;
Entry next;
/**
* Creates new entry.
*/
Entry(Object key, V value,
ReferenceQueue queue,
int hash, Entry next) {
/**
* 这句super(key,queue);
* 就把Entry对象引用队列关联了起来。
* 此时的Entry对象是弱引用对象,弱引用WeakReference
* 的构造器new WeakReference(o,q)
* 意思就是WeakReference对象引用o对象,当弱引用被垃圾回收
* 则o对象就没有引用了,也会被垃圾回收,此时WeakReference对象
* 会被加入到引用队列queue当中去。
* 在这里,我们可以吧Entry对象看作是增强了参数的WeakReference对象。
*/
super(key, queue);
this.value = value;
this.hash = hash;
this.next = next;
}
@SuppressWarnings("unchecked")
public K getKey() {
return (K) WeakHashMap.unmaskNull(get());
}
public V getValue() {
return value;
}
public V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry e = (Map.Entry)o;
K k1 = getKey();
Object k2 = e.getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
V v1 = getValue();
Object v2 = e.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2)))
return true;
}
return false;
}
public int hashCode() {
K k = getKey();
V v = getValue();
return ((k==null ? 0 : k.hashCode()) ^
(v==null ? 0 : v.hashCode()));
}
public String toString() {
return getKey() + "=" + getValue();
}
}这是WeakHashMap类中内部类Entry的定义,这也就是它与普通HashMap不同的关键所在。Entry构造器中,将每个Entry与引用队列关联,而每个Entry对象就是一个弱引用!!!这个弱引用指向key值。
就不详解了,关于引用,查看: http://blog.csdn.net/ochangwen/article/details/51406779#t3
/**
* Expunges stale entries from the table.
* 该方法是WeakHashMap类的核心方法
* 每次在进行getSize() getTable()方法是都要调用该方法
* 该方法实现的功能是:
* 通过遍历引用队列当中保存的已经回收的弱引用对象
* 将原map 数组中的引用清除,map数组中只保留还没有回收的弱引用对象。
* queue.poll()弹出的是弱引用对象,该类中的Entry集成了WeakReference类
* 方法中利用两层循环:一层循环遍历引用队列中的值,另一层循环遍历
* map数组中的值,当在map数组中发现由于引用队列中相同的引用
* 则把应用变量从map数组中删除。更新map数组长度
*/
private void expungeStaleEntries() {
for (Object x; (x = queue.poll()) != null; ) {
synchronized (queue) {
@SuppressWarnings("unchecked")
Entry e = (Entry) x;
int i = indexFor(e.hash, table.length);
Entry prev = table[i];
Entry p = prev;
while (p != null) {
Entry next = p.next;
if (p == e) {
if (prev == e)
table[i] = next;
else
prev.next = next;
// Must not null out e.next;
// stale entries may be in use by a HashIterator
e.value = null; // Help GC
size--;
break;
}
prev = p;
p = next;
}
}
}
}
三、EnumMap
EnumMap是与枚举类相结合的Map类。跟hash没有多大关系。EnumMap就是专门与枚举类结合形成Map的key-value对结构。值的注意的是,EnumMap中虽然也存储的是key-value对的数据,但是内存实现上却采用的是数组结构。key存储一个数组结构,value也用一个对应的数组结构。
private enum Season
{
SPRING,SUMMER,FALL,WINTER
}
@Test
public void testEnumMap() {
//创建一个EnumMap对象,该EnumMap的所有key必须是Season枚举类的枚举值
EnumMap enumMap = new EnumMap(Season.class);
enumMap.put(Season.SUMMER,"夏日炎炎");
enumMap.put(Season.SPRING,"春暧花开");
System.out.println(enumMap); //{SPRING=春暧花开, SUMMER=夏日炎炎}
}
构造器
public EnumMap(Class keyType) {
this.keyType = keyType;
//保存枚举类的所有枚举值到数组中
keyUniverse = getKeyUniverse(keyType);
vals = new Object[keyUniverse.length];
}
public EnumMap(EnumMap m) {
keyType = m.keyType;
keyUniverse = m.keyUniverse;
vals = m.vals.clone();
size = m.size;
}
public EnumMap(Map m) {
//如果m类是EnumMap类,与上面的构造器一样
if (m instanceof EnumMap) {
EnumMap em = (EnumMap) m;
keyType = em.keyType;
keyUniverse = em.keyUniverse;
vals = em.vals.clone();
size = em.size;
} else {
//如果不是EnumMap类,则不允许vals数组长度为空
if (m.isEmpty())
throw new IllegalArgumentException("Specified map is empty");
keyType = m.keySet().iterator().next().getDeclaringClass();
keyUniverse = getKeyUniverse(keyType);
vals = new Object[keyUniverse.length];
putAll(m);
}
}
上面是EnumMap的三个构造器,有构造器知道,该类存储的key-value对,key值类型必须是枚举类型。枚举类型的值通过该方法getKeyUniverse(keyType)获取枚举类数组,keyUniverse 数组存放key集合。vals数组存放value数据。
-------------------------------------------------------------------------------------------------------------
put方法
public V put(K key, V value) {
typeCheck(key);
int index = key.ordinal();
Object oldValue = vals[index];
vals[index] = maskNull(value);
if (oldValue == null)
size++;
return unmaskNull(oldValue);
}
private void typeCheck(K key) {
Class keyClass = key.getClass();
if (keyClass != keyType && keyClass.getSuperclass() != keyType)
throw new ClassCastException(keyClass + " != " + keyType);
}
put方法存入key-value对。由于key值是固定的枚举类的常量值,并且在构造器初始化的过程中,已经获取到了,这里put方法中在此传入key值,是为了vals数组中保存value值。
首先检查key类型是否是构造器初始化过程中绑定的枚举类型,如果不是,抛出异常ClassCastException。
然后又key值,计算value存储在vals数组中的索引值,然后将value值存入。如果原来已有value值,则替换原来的value值。
由此可见,vals长度最多只能是枚举类常量值的个数。key值只能是枚举类常量值。
通过put方法我们已经基本了解了EnumMap类的特点了,其他方法都是按照这个思想进行的。
public void putAll(Map m) {
if (m instanceof EnumMap) {
EnumMap em =
(EnumMap)m;
if (em.keyType != keyType) {
if (em.isEmpty())
return;
throw new ClassCastException(em.keyType + " != " + keyType);
}
for (int i = 0; i < keyUniverse.length; i++) {
Object emValue = em.vals[i];
if (emValue != null) {
if (vals[i] == null)
size++;
vals[i] = emValue;
}
}
} else {
/**
* 这里的putAll(m)会调用父类的方法
* 父类该方法的实现上,均采用一一放入的put方法进行
* 数据的存入。
* 此时会调用put(e.getKey(), e.getValue());
* 子类put方法会被调用,所以回到了该类中的put方法
* put方法中会检验key值的类型,如果不匹配就不能放入,
* 抛出异常
*/
super.putAll(m);
}
}
keyUniverse就是初始化时保存的枚举类常量值数组。通过上面的方法,非常简单保存集合中的数据。并且EnumMap采用数组保存key-value对,管理起来很方便,逻辑不复杂。
-------------------------------------------------------------------------------
get方法
public V get(Object key) {
return (isValidKey(key) ?
unmaskNull(vals[((Enum)key).ordinal()]) : null);
}
/**
* Returns true if key is of the proper type to be a key in this
* enum map.
* 检查key值是否是有效值
* key值不能为null
* 同时key的类型必须是初始化时的枚举类类型或者
* 其子类,否则就是无效值
*/
private boolean isValidKey(Object key) {
if (key == null)
return false;
// Cheaper than instanceof Enum followed by getDeclaringClass
Class keyClass = key.getClass();
return keyClass == keyType || keyClass.getSuperclass() == keyType;
}
get方法很简单,只要key传入的合理的参数,很快就能得到结果。O(1)的时间复杂度。通过上面的put方法和get方法,结合起来看,EnumMap类进行key-value保存的时候,key专门用一个数组进行保存,然后vaule值存放在数组vals的相对应的位置上。当需要获取指定key的value时,直接通过key即可得到数组vals对应位置上的值。并且不会出现冲突的问题。因为Enum类的各个枚举值不相同。
4、删除方法
public V remove(Object key) {
if (!isValidKey(key))
return null;
int index = ((Enum)key).ordinal();
Object oldValue = vals[index];
vals[index] = null;
if (oldValue != null)
size--;
return unmaskNull(oldValue);
}
private boolean removeMapping(Object key, Object value) {
if (!isValidKey(key))
return false;
int index = ((Enum)key).ordinal();
if (maskNull(value).equals(vals[index])) {
vals[index] = null;
size--;
return true;
}
return false;
}
/**
* Returns true if key is of the proper type to be a key in this
* enum map.
* 检查key值是否是有效值
* key值不能为null
* 同时key的类型必须是初始化时的枚举类类型或者
* 其子类,否则就是无效值
*/
private boolean isValidKey(Object key) {
if (key == null)
return false;
// Cheaper than instanceof Enum followed by getDeclaringClass
Class keyClass = key.getClass();
return keyClass == keyType || keyClass.getSuperclass() == keyType;
}
删除方法很简单,key值是不会删除的,因为key值是枚举类常量值,只会把vals数组中对应位置上的value值删除。
remove(key)是把key值对应位置上vals数组中的值删除。
removeMapping(key,value)是把对应位置上与valus相等的vals数组中的数据删除。如果不等,不删除。
5 是否存在指定的数据方法
/**
* @param key the key whose presence in this map is to be tested
* @return true if this map contains a mapping for the specified
* 首先检验key值是否是有效值
* 然后获取对应的key值的value值是否为null
* 这里需要注意的是,put方法存放key-value对的时候,
* 如果value值为null,则会通过maskNull()方法将null值,转换成NULL对象
*所以实际存入vals数组中的value值不为null值。
*/
public boolean containsKey(Object key) {
return isValidKey(key) && vals[((Enum)key).ordinal()] != null;
}
private boolean containsMapping(Object key, Object value) {
return isValidKey(key) &&
maskNull(value).equals(vals[((Enum)key).ordinal()]);
}
上面两个方法看明白的话,就很容易明白EnumMap类是如何进行key-value对的管理的。key值初始化的时候已经获取,保存在了数组中,value值是通过put或者putAll方法添加到数组中的,containsKey(Object key)方法不是通过key值数组判断的,而是通过key值对应位置上的value值是否存在进行判断的。也就是说key值对应的vals数组中是否有值,决定了EnumMap类对象是否包含key值。
EnumMap类的其他方法就没什么说的了,大家看看就可以了,只要弄懂了HashMap类,其他的类就不在话下了。
你可能感兴趣的:(Java集合框架)
Java 集合:单列集合和双列集合的深度剖析
刘小炮吖i
Java后端开发面试题 Java 集合 java
引言在Java编程中,集合是一个非常重要的概念。它就像是一个容器,能够存储多个数据元素,帮助我们更方便地管理和操作数据。Java集合框架主要分为单列集合和双列集合两大类,它们各自有着独特的特点和适用场景。接下来,让我们深入探究这两种集合。单列集合单列集合就像是一列整齐排列的数据队伍,每个元素都是独立的个体,按照一定的规则存储和组织。在Java中,单列集合的根接口是java.util.Collect
Java集合框架与线程安全:深入解析与最佳实践
bdawn
java java 开发语言 集合 多线程 线程 安全 list
目录一、Java集合框架概览二、线程安全挑战与解决方案典型线程安全问题示例传统同步方案现代并发集合解析三、性能对比与选型策略基准测试数据(单位:ops/ms)选型决策树四、最佳实践与陷阱规避五、未来演进趋势结语一、Java集合框架概览Java集合框架(JavaCollectionsFramework)是Java开发者最常用的工具包之一,其核心接口构成清晰的层级体系:List接口:有序可重复集合Ar
Java集合类归纳+思维导图
web2u
Java 基础 java 开发语言
Java集合框架主要分为两大类:Collection接口和Map接口。Collection接口(存储对象)分为三大类:Set:HashSetLinkedHashSet(基于链表和哈希表)TreeSetQueue:PriorityQueue(基于优先级,元素按自然排序或指定比较器排序)LinkedList(作为队列使用)List:ArrayListLinkedListVectorMap接口(存储键值
Java——列表(List)
不会Hello World的小苗
Java java list python
概述在Java中,列表(List)是一种有序的集合,它允许元素重复,并且每个元素都有一个对应的索引值。Java提供了List接口及其实现类,用于表示和操作列表数据。常用的实现类包括ArrayList、LinkedList和Vector。1、List接口概述List是Java集合框架中的一种接口,继承自Collection接口。它定义了许多常见的操作,如:添加元素:add(Ee)、add(intin
吐血整理Java集合框架,免费送
聪明马的博客
Java java 数据结构
Java集合框架(JavaCollectionsFramework)是Java标准库中的一个重要部分。它为Java开发人员提供了一组常用的数据结构,如列表、集合、映射等,使其更容易地处理数据。在这篇博客中,我将详细介绍Java集合框架,包括它的主要特点、常用的集合类型以及如何使用它们来解决实际问题。一、Java集合框架的主要特点Java集合框架的主要特点是:统一的接口。Java集合框架提供了一组统
深入理解Java的集合框架
一碗黄焖鸡三碗米饭
java
深入理解Java的集合框架Java集合框架(JavaCollectionsFramework,简称JCF)是Java语言中最常用的API之一,它为开发者提供了强大且灵活的数据结构支持。集合框架通过一系列的接口和实现类,帮助我们管理、存储和操作数据。Java集合框架包括常见的List、Set、Map等接口及其具体实现类,合理选择适当的集合类型,对于程序性能和代码可维护性至关重要。本文将深入解析Jav
Java 编码系列:集合框架(List、Set、Map 及其常用实现类)
pjx987
java编码 java list map
引言在Java开发中,集合框架是不可或缺的一部分,它提供了存储和操作一组对象的工具。Java集合框架主要包括List、Set和Map接口及其常用的实现类。正确理解和使用这些集合类不仅可以提高代码的可读性和性能,还能避免一些常见的错误。本文将深入探讨Java集合框架的底层原理,并结合大厂的最佳实践,帮助读者掌握这些核心概念。1.List接口及其常用实现类1.1基本概念List接口表示一个有序的集合,
红 - 黑树和 B+树?
百态老人
笔记
红黑树是一种自平衡二叉查找树,由RudolfBayer发明,在1978年被LeoJ.Guibas和RobertSedgewick改称为“红黑树”。它的特点包括每个节点非红即黑;根节点是黑色;每个叶子节点都是黑色的空节点;如果一个节点是红色的,那么它的两个子节点都是黑色;从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。在Java集合框架中,很多部分如HashMap、TreeMap、Tree
JAVA面试题
qq_40055200
java 开发语言
1.简述什么是值传递和引用传递?值传递:方法调用时,实际参数把它的值传递给对应的形式参数,方法执行中形式参数值的改变不影响实际参数的值。引用传递:也称为传地址。方法调用时,实际参数的引用(地址,而不是参数的值)被传递给方法中相对应的形式参数,在方法执行中,对形式参数的操作实际上就是对实际参数的操作,方法执行中形式参数值的改变将会影响实际参数的值。2.简述什么是迭代器是Java集合框架中的一种机制,
【设计模式】【行为型模式】迭代器模式(Iterator)
FLZJ_KL
设计模式 设计模式 迭代器模式 java
hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD2025本人正在沉淀中…博客更新速度++欢迎点赞、收藏、关注,跟上我的更新节奏当你的天空突然下了大雨,那是我在为你炸乌云文章目录一、入门什么是迭代器模式?为什么要迭代器模式?怎么实现迭代器模式?二、迭代器模式在源码中的运用Java集合框架(JavaCollectionsFramework)Java集合框架迭代器的使用
ArrayList 和 LinkedList区别
sillyyyy
链表 数据结构 java
ArrayList和LinkedList是Java集合框架中两种不同的List实现,它们的区别如下:底层数据结构不同:ArrayList是基于动态数组实现的,而LinkedList是基于双向链表实现的。因此,在对数据进行随机访问或者遍历时,ArrayList的性能要优于LinkedList,而在对数据进行插入或者删除操作时,LinkedList的性能要优于ArrayList。内存使用方式不同:由于
Java面试第一山!《集合》!
TFHoney
面试 职场和发展
一、引言在Java编程的世界里,数据的存储和处理是非常重要的环节。Java集合框架就像是一个功能强大的工具箱,为我们提供了各种各样的数据结构来高效地存储和操作数据。今天,跟随小编一起来深入了解Java集合框架,这不仅有助于你在日常开发中更加得心应手,还能在面试中脱颖而出。二、Java集合框架概述Java集合框架主要分为两大体系:Collection和Map。Collection接口是存储单个元素的
【Java基础-44.7】Java 中的 Map 集合接口:实现类、继承关系及常用方法详解
AllenBright
# Java基础 java 开发语言
在Java中,Map是一种非常重要的集合接口,用于存储键值对(Key-ValuePair)。它提供了高效的键值查找和操作能力,广泛应用于各种场景中。本文将深入探讨Map接口的实现类、继承关系以及常用方法,帮助读者更好地理解和使用Map。1.Map接口概述Map是Java集合框架中的一员,位于java.util包中。它表示一组键值对的集合,其中每个键(Key)都是唯一的,而值(Value)可以重复。
Java 中 HashMap 的工作原理
静默.\\
java
Java中HashMap的工作原理HashMap是Java集合框架中一个非常重要的类,它实现了Map接口,用于存储键值对(key-valuepairs)。HashMap允许我们根据键快速查找、插入和删除值。其核心特点是基于哈希表实现,提供了平均时间复杂度为O(1)的基本操作。基本结构HashMap主要由数组和链表或红黑树组成。具体来说:数组:HashMap内部维护了一个Entry数组,默认大小为1
Java集合01:初识集合以及Collection接口方法详解
金士曼
JavaSE java 数据结构 开发语言
⭕️前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家(点击跳转到网站)⭕️文章目录一、Java集合框架概述1.基础知识2.两种体系二、Collection接口方法简单常用方法(5个)contains方法containsAll方法remove方法removeAll方法retainAll方法equals方法hashCode方法:toArray方法附加:数组和集合的转换i
双端链表 LinkedList
JFS_Study
DataStructure 数据结构
一、LinkedList介绍1️⃣LinkedList是Java集合框架中一个重要的实现,其底层采用的双向链表结构,没有初始化大小,就是一直在前面或者后面新增就好。由于基于链表实现,存储元素过程中,无需像ArrayList那样进行扩容。2️⃣LinkedList存储元素的节点需要额外的空间存储前驱和后继的引用。3️⃣LinkedList在链表头部和尾部插入效率比较高,但在指定位置进行插入时,效率一
【Java基础-44.2】Java中的LinkedList:特征与方法详解
AllenBright
# Java基础 java 开发语言
在Java集合框架中,LinkedList是一个非常重要的数据结构,它实现了List和Deque接口,提供了双向链表的实现。与ArrayList不同,LinkedList在插入和删除操作上具有更高的效率,但在随机访问元素时性能较差。本文将深入探讨LinkedList的特征及其常用方法的应用。1.LinkedList的特征1.1双向链表结构LinkedList是基于双向链表实现的,每个节点(Node
Java基础面试宝典:全面掌握Java编程核心技术
心言星愿
本文还有配套的精品资源,点击获取简介:这份《JAVA基础面试大全》是一份集结了Java基础知识、面向对象特性、异常处理、核心API和常用开发工具等内容的资料,是求职者准备Java编程面试的必备指南。它详细讲解了数据类型、变量、运算符、流程控制等基础概念;类与对象、封装、继承和多态等面向对象的核心概念;异常处理机制;以及Java集合框架、IO流、多线程编程等核心API。此外,还包括对开发工具和源码阅
Java 集合框架:HashMap 的介绍、使用、原理与源码解析
NicoleGus
哈希算法 散列表 算法
一、HashMap介绍HashMap是Java集合框架中最常用的数据结构之一。它实现了Map接口,允许我们以键值对的形式存储数据。HashMap的主要特点是通过哈希表(HashTable)来实现对数据的高效查找、插入和删除操作。1.特性无序存储:HashMap并不保证元素的顺序,元素的顺序可能会随着插入的顺序和哈希冲突的解决方式而变化。允许null键和null值:HashMap允许一个null键和
剖根问底:Java 不能实现真正泛型的原因是什么?
沉默王二
Java进阶之路 Java程序员进阶之路 java 编程语言
大家好,我是二哥呀!今天我来给大家讲一下,Java不能实现真正泛型的原因是什么?本文已同步至GitHub《教妹学Java》专栏,风趣幽默,通俗易懂,对Java初学者亲切友善,么么哒,内容包括Java语法、Java集合框架、Java并发编程、Java虚拟机等核心知识点,欢迎star。GitHub开源地址:https://github.com/itwanger/jmx-javaCodeChina:ht
Java 中 List 源码解析:深度剖析与实现
HelloZheQ
java list windows
List是Java中最常用的数据结构之一,广泛用于存储有序的元素集合。它是Java集合框架中的一个接口,提供了多种常见的实现,如ArrayList、LinkedList、Vector等。通过对List接口及其常见实现类的源码分析,开发者可以深入理解其内部机制和实现方式,进而优化应用程序的性能,做出更合适的选择。本文将通过深入解析List接口及其常见实现(特别是ArrayList和LinkedLis
ArrayList 源码解析
程序猿进阶
Java基础 ArrayList List java 面试 性能优化 架构设计 idea
ArrayList是Java集合框架中的一个动态数组实现,提供了可变大小的数组功能。它继承自AbstractList并实现了List接口,是顺序容器,即元素存放的数据与放进去的顺序相同,允许放入null元素,底层通过数组实现。除该类未实现同步外,其余跟Vector大致相同。每个ArrayList都有一个容量capacity,表示底层数组的实际大小,容器内存储元素的个数不能多于当前容量。当向容器中添
互联网 Java 工程师面试题(Java 面试题四)
苹果酱0567
面试题汇总与解析 java 中间件 开发语言 spring boot 后端
下面列出这份Java面试问题列表包含的主题多线程,并发及线程基础数据类型转换的基本原则垃圾回收(GC)Java集合框架数组字符串GOF设计模式SOLID抽象类与接口Java基础,如equals和hashcode泛型与枚举JavaIO与NIO常用网络协议Java中的数据结构和算法正则表达式JVM底层Java最佳实JDBCDate,Time与CalendarJava处理XMLJUnit编程现在是时候给
java----TreeMap
qq_44766305
数据结构
TreeMap.TreeMap跟TreeSet底层原理一样,都是红黑树结构的.由键决定特性:不重复、无索引、可排序.可排序:对键进行排序.注意:默认按照键从小到大进行排序,也可以按照自己规定键的排序规则代码书写两种排序规则:1.实现Comparable接口,指定比较规则2.创建集合时传递Compartor比较器对象,指定比较规则Comparable接口是Java集合框架的一部分,它允许对象定义它们
深入理解 Java 中 Map 和 Set 接口的高级用法
项目笔记与工具库
java 开发语言
Java中的Map和Set接口是两个非常重要的数据结构,它们在日常开发中被广泛使用。本文将深入探讨这两个接口的高级用法,特别是如何自定义键的比较方式以及实现高效的集合操作。这些技巧能够帮助开发者更好地应对复杂的数据处理场景,并提升程序的性能。一、Map接口的高级用法Map接口用于存储键值对,是Java集合框架中最常用的数据结构之一。在某些场景下,默认的键比较方式可能不满足需求,因此了解如何自定义键
Java之Java基础二十(集合[上])
Violet永存
Java java 开发语言 Java基础
Java集合框架可以分为两条大的支线:①、Collection,主要由List、Set、Queue组成:List代表有序、可重复的集合,典型代表就是封装了动态数组的ArrayList和封装了链表的LinkedList;Set代表无序、不可重复的集合,典型代表就是HashSet和TreeSet;Queue代表队列,典型代表就是双端队列ArrayDeque,以及优先级队列PriorityQueue。②
面试:说一下HashMap的底层实现原理,我懵了
一只程序猿哟
哈希表(hashtable)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出现在各类的面试题中,重要性可见一斑。本文会对java集合框架中的对应实现HashMap的实现原理进行讲解,然后会对JDK7的HashMap源码进行分析(JDK8会有所不同,需要了解的可自行阅读JDK8的
java进阶学习
伟主教
java 学习 开发语言
学习Java进阶知识需要系统地了解和掌握一些高级概念和技术,这些包括但不限于多线程、集合框架、输入输出、网络编程、数据库连接、JVM原理等。以下是一个详细的学习路线和资源建议,帮助您更好地掌握Java的高级部分。学习路线深入理解Java基础Java核心语法和基础:巩固基础知识,包括面向对象编程(OOP)的概念。集合框架:深入学习Java集合框架中的各种接口和实现类,例如ArrayList、Link
第四章 Java核心类库 第三节 集合框架
爱掉发的小龙
java windows 开发语言 学习 笔记
1.集合框架概述与结构首先,我们来简单了解一下Java集合框架的概述和结构。集合框架的定义:Java集合框架是一组用来存储和操作数据集合的接口和类。它提供了一种统一的标准方法来操作不同的数据集合,极大简化了编程任务。集合框架的结构:集合框架包括三大主要接口:List接口:用于存储有序且可以重复的元素。Set接口:用于存储无序且不重复的元素。Map接口:用于存储键值对,键不能重复但值可以重复。这些接
JAVA高级编程之集合框架和泛型(超详细)
就这个java爽!
开发语言 java eclipse idea 青少年编程
Java集合框架包含的内容Java集合框架提供了一套性能优良、使用方便的接口和类,它们位于java.util包中Collection接口存储一组不唯一,无序的对象List接口存储一组不唯一,有序(插入顺序)的对象Set接口存储一组唯一,无序的对象Map接口存储一组键值对象,提供key到value的映射List接口的实现类ArrayList实现了长度可变的数组,在内存中分配连续的空间,遍历元素和随机
Enum 枚举
120153216
enum 枚举
原文地址:http://www.cnblogs.com/Kavlez/p/4268601.html Enumeration
于Java 1.5增加的enum type...enum type是由一组固定的常量组成的类型,比如四个季节、扑克花色。在出现enum type之前,通常用一组int常量表示枚举类型。比如这样:
public static final int APPLE_FUJI = 0
Java8简明教程
bijian1013
java jdk1.8
Java 8已于2014年3月18日正式发布了,新版本带来了诸多改进,包括Lambda表达式、Streams、日期时间API等等。本文就带你领略Java 8的全新特性。
一.允许在接口中有默认方法实现
Java 8 允许我们使用default关键字,为接口声明添
Oracle表维护 快速备份删除数据
cuisuqiang
oracle 索引 快速 备份 删除
我知道oracle表分区,不过那是数据库设计阶段的事情,目前是远水解不了近渴。
当前的数据库表,要求保留一个月数据,且表存在大量录入更新,不存在程序删除。
为了解决频繁查询和更新的瓶颈,我在oracle内根据需要创建了索引。但是随着数据量的增加,一个半月数据就要超千万,此时就算有索引,对高并发的查询和更新来说,让然有所拖累。
为了解决这个问题,我一般一个月会进行一次数据库维护,主要工作就是备
java多态内存分析
麦田的设计者
java 内存分析 多态原理 接口和抽象类
“ 时针如果可以回头,熟悉那张脸,重温嬉戏这乐园,墙壁的松脱涂鸦已经褪色才明白存在的价值归于记忆。街角小店尚存在吗?这大时代会不会牵挂,过去现在花开怎么会等待。
但有种意外不管痛不痛都有伤害,光阴远远离开,那笑声徘徊与脑海。但这一秒可笑不再可爱,当天心
Xshell实现Windows上传文件到Linux主机
被触发
windows
经常有这样的需求,我们在Windows下载的软件包,如何上传到远程Linux主机上?还有如何从Linux主机下载软件包到Windows下;之前我的做法现在看来好笨好繁琐,不过也达到了目的,笨人有本方法嘛;
我是怎么操作的:
1、打开一台本地Linux虚拟机,使用mount 挂载Windows的共享文件夹到Linux上,然后拷贝数据到Linux虚拟机里面;(经常第一步都不顺利,无法挂载Windo
类的加载ClassLoader
肆无忌惮_
ClassLoader
类加载器ClassLoader是用来将java的类加载到虚拟机中,类加载器负责读取class字节文件到内存中,并将它转为Class的对象(类对象),通过此实例的 newInstance()方法就可以创建出该类的一个对象。
其中重要的方法为findClass(String name)。
如何写一个自己的类加载器呢?
首先写一个便于测试的类Student
html5写的玫瑰花
知了ing
html5
<html>
<head>
<title>I Love You!</title>
<meta charset="utf-8" />
</head>
<body>
<canvas id="c"></canvas>
google的ConcurrentLinkedHashmap源代码解析
矮蛋蛋
LRU
原文地址:
http://janeky.iteye.com/blog/1534352
简述
ConcurrentLinkedHashMap 是google团队提供的一个容器。它有什么用呢?其实它本身是对
ConcurrentHashMap的封装,可以用来实现一个基于LRU策略的缓存。详细介绍可以参见
http://code.google.com/p/concurrentlinke
webservice获取访问服务的ip地址
alleni123
webservice
1. 首先注入javax.xml.ws.WebServiceContext,
@Resource
private WebServiceContext context;
2. 在方法中获取交换请求的对象。
javax.xml.ws.handler.MessageContext mc=context.getMessageContext();
com.sun.net.http
菜鸟的java基础提升之道——————>是否值得拥有
百合不是茶
1,c++,java是面向对象编程的语言,将万事万物都看成是对象;java做一件事情关注的是人物,java是c++继承过来的,java没有直接更改地址的权限但是可以通过引用来传值操作地址,java也没有c++中繁琐的操作,java以其优越的可移植型,平台的安全型,高效性赢得了广泛的认同,全世界越来越多的人去学习java,我也是其中的一员
java组成:
通过修改Linux服务自动启动指定应用程序
bijian1013
linux
Linux中修改系统服务的命令是chkconfig (check config),命令的详细解释如下: chkconfig
功能说明:检查,设置系统的各种服务。
语 法:chkconfig [ -- add][ -- del][ -- list][系统服务] 或 chkconfig [ -- level <</SPAN>
spring拦截器的一个简单实例
bijian1013
java spring 拦截器 Interceptor
Purview接口
package aop;
public interface Purview {
void checkLogin();
}
Purview接口的实现类PurviesImpl.java
package aop;
public class PurviewImpl implements Purview {
public void check
[Velocity二]自定义Velocity指令
bit1129
velocity
什么是Velocity指令
在Velocity中,#set,#if, #foreach, #elseif, #parse等,以#开头的称之为指令,Velocity内置的这些指令可以用来做赋值,条件判断,循环控制等脚本语言必备的逻辑控制等语句,Velocity的指令是可扩展的,即用户可以根据实际的需要自定义Velocity指令
自定义指令(Directive)的一般步骤
&nbs
【Hive十】Programming Hive学习笔记
bit1129
programming
第二章 Getting Started
1.Hive最大的局限性是什么?一是不支持行级别的增删改(insert, delete, update)二是查询性能非常差(基于Hadoop MapReduce),不适合延迟小的交互式任务三是不支持事务2. Hive MetaStore是干什么的?Hive persists table schemas and other system metadata.
nginx有选择性进行限制
ronin47
nginx 动静 限制
http {
limit_conn_zone $binary_remote_addr zone=addr:10m;
limit_req_zone $binary_remote_addr zone=one:10m rate=5r/s;...
server {...
location ~.*\.(gif|png|css|js|icon)$ {
java-4.-在二元树中找出和为某一值的所有路径 .
bylijinnan
java
/*
* 0.use a TwoWayLinkedList to store the path.when the node can't be path,you should/can delete it.
* 1.curSum==exceptedSum:if the lastNode is TreeNode,printPath();delete the node otherwise
Netty学习笔记
bylijinnan
java netty
本文是阅读以下两篇文章时:
http://seeallhearall.blogspot.com/2012/05/netty-tutorial-part-1-introduction-to.html
http://seeallhearall.blogspot.com/2012/06/netty-tutorial-part-15-on-channel.html
我的一些笔记
===
js获取项目路径
cngolon
js
//js获取项目根路径,如: http://localhost:8083/uimcardprj
function getRootPath(){
//获取当前网址,如: http://localhost:8083/uimcardprj/share/meun.jsp
var curWwwPath=window.document.locati
oracle 的性能优化
cuishikuan
oracle SQL Server
在网上搜索了一些Oracle性能优化的文章,为了更加深层次的巩固[边写边记],也为了可以随时查看,所以发表这篇文章。
1.ORACLE采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之前,那些可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾。(这点本人曾经做过实例验证过,的确如此哦!
Shell变量和数组使用详解
daizj
linux shell 变量 数组
Shell 变量
定义变量时,变量名不加美元符号($,PHP语言中变量需要),如:
your_name="w3cschool.cc"
注意,变量名和等号之间不能有空格,这可能和你熟悉的所有编程语言都不一样。同时,变量名的命名须遵循如下规则:
首个字符必须为字母(a-z,A-Z)。
中间不能有空格,可以使用下划线(_)。
不能使用标点符号。
不能使用ba
编程中的一些概念,KISS、DRY、MVC、OOP、REST
dcj3sjt126com
REST
KISS、DRY、MVC、OOP、REST (1)KISS是指Keep It Simple,Stupid(摘自wikipedia),指设计时要坚持简约原则,避免不必要的复杂化。 (2)DRY是指Don't Repeat Yourself(摘自wikipedia),特指在程序设计以及计算中避免重复代码,因为这样会降低灵活性、简洁性,并且可能导致代码之间的矛盾。 (3)OOP 即Object-Orie
[Android]设置Activity为全屏显示的两种方法
dcj3sjt126com
Activity
1. 方法1:AndroidManifest.xml 里,Activity的 android:theme 指定为" @android:style/Theme.NoTitleBar.Fullscreen" 示例: <application
solrcloud 部署方式比较
eksliang
solrCloud
solrcloud 的部署其实有两种方式可选,那么我们在实践开发中应该怎样选择呢? 第一种:当启动solr服务器时,内嵌的启动一个Zookeeper服务器,然后将这些内嵌的Zookeeper服务器组成一个集群。 第二种:将Zookeeper服务器独立的配置一个集群,然后将solr交给Zookeeper进行管理
谈谈第一种:每启动一个solr服务器就内嵌的启动一个Zoo
Java synchronized关键字详解
gqdy365
synchronized
转载自:http://www.cnblogs.com/mengdd/archive/2013/02/16/2913806.html
多线程的同步机制对资源进行加锁,使得在同一个时间,只有一个线程可以进行操作,同步用以解决多个线程同时访问时可能出现的问题。
同步机制可以使用synchronized关键字实现。
当synchronized关键字修饰一个方法的时候,该方法叫做同步方法。
当s
js实现登录时记住用户名
hw1287789687
记住我 记住密码 cookie 记住用户名 记住账号
在页面中如何获取cookie值呢?
如果是JSP的话,可以通过servlet的对象request 获取cookie,可以
参考:http://hw1287789687.iteye.com/blog/2050040
如果要求登录页面是html呢?html页面中如何获取cookie呢?
直接上代码了
页面:loginInput.html
代码:
<!DOCTYPE html PUB
开发者必备的 Chrome 扩展
justjavac
chrome
Firebug:不用多介绍了吧https://chrome.google.com/webstore/detail/bmagokdooijbeehmkpknfglimnifench
ChromeSnifferPlus:Chrome 探测器,可以探测正在使用的开源软件或者 js 类库https://chrome.google.com/webstore/detail/chrome-sniffer-pl
算法机试题
李亚飞
java 算法 机试题
在面试机试时,遇到一个算法题,当时没能写出来,最后是同学帮忙解决的。
这道题大致意思是:输入一个数,比如4,。这时会输出:
&n
正确配置Linux系统ulimit值
字符串
ulimit
在Linux下面部 署应用的时候,有时候会遇上Socket/File: Can’t open so many files的问题;这个值也会影响服务器的最大并发数,其实Linux是有文件句柄限制的,而且Linux默认不是很高,一般都是1024,生产服务器用 其实很容易就达到这个数量。下面说的是,如何通过正解配置来改正这个系统默认值。因为这个问题是我配置Nginx+php5时遇到了,所以我将这篇归纳进
hibernate调用返回游标的存储过程
Supanccy2013
java DAO oracle Hibernate jdbc
注:原创作品,转载请注明出处。
上篇博文介绍的是hibernate调用返回单值的存储过程,本片博文说的是hibernate调用返回游标的存储过程。
此此扁博文的存储过程的功能相当于是jdbc调用select 的作用。
1,创建oracle中的包,并在该包中创建的游标类型。
---创建oracle的程
Spring 4.2新特性-更简单的Application Event
wiselyman
application
1.1 Application Event
Spring 4.1的写法请参考10点睛Spring4.1-Application Event
请对比10点睛Spring4.1-Application Event
使用一个@EventListener取代了实现ApplicationListener接口,使耦合度降低;
1.2 示例
包依赖
<p