A Simple Ordered Hashtable

This class implements an ordered hashtable, which maps keys to values. Any non-null object can be used as a key or as a value.

Because in the MIDP API there is no support for the Java Collection API which provides a rich set of collection classes, SimpleOrderedHashtable.java implements, as the name implies, a simple ordered Hashtable. You can use this SimpleOrderedHashtable as a container of objects that exposes both hashed and chronologically ordered sequential access capabilities.

This ordered Hashtable is called simple because internally it uses the Legacy collection classes, a Vector to maintain the element's order and a Hashtable to provide hashing capabilities. Because Hashtable and Vector grow differently, the implementation of com.j2medeveloper.util.SimpleOrderedHashtable is not the most efficient one, but may be good enough for your needs.

As with typical Hashtables, to successfully store and retrieve objects from a hashtable, the objects used as keys must implement the hashCode method and the equals method.

There are many instances where you would like to use an ordered Hashtable, for example, to keep your user interface elements ordered, or to keep ordered items from a database or backend while keeping rapid access via Hashtable keys, or to store and access any value you want to access using a key.

The Code - SimpleOrderedHashtable.java

/* -----------------------------------------------------------------------------
 * SimpleOrderedHashtable.java
 * Author: C. Enrique Ortiz
 * Copyright (c) 2004-2005 C. Enrique Ortiz <eortiz@j2medeveloper.com>
 *
 * SimpleOrderedHashtable.java implements a simple Hashtable that is
 * chronologically ordered.
 *
 * This is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option) any
 * later version.
 *
 * Usage & redistributions of source code must retain the above copyright notice.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should get a copy of the GNU Lesser General Public License from
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA  02111-1307  USA
 * -----------------------------------------------------------------------------
 */

package com.j2medeveloper.util;

import java.util.Vector;
import java.util.Enumeration;
import java.util.Hashtable;

/**
 *  Implements an Ordered Hashtable, with elements in
 *    chronological order (i.e. insertion order)
 */
public class SimpleOrderedHashtable {

    private Vector    orderedKeys;
    private Hashtable hashTable;

    /**
     *  Constructor, creates an SimpleOrderedHashtable.
     */
    public SimpleOrderedHashtable() {
        orderedKeys = new Vector();
        hashTable = new Hashtable();
    }

    /**
     *  Constructor, creates an SimpleOrderedHashtable.
     *  @param initialCapacity is the initial size for the container.
     */
    public SimpleOrderedHashtable(int initialCapacity) {
        orderedKeys = new Vector(initialCapacity);
        hashTable = new Hashtable(initialCapacity);
    }

    /**
     *  Maps the specified key to the specified value in this SimpleOrderedHashtable.
     *  The value can be retrieved by calling the get method with a key that is
     *  equal to the original key.
     *  @param key is the hashtable key.
     *  @param value is the value.
     *  @return the previous value of the specified key in this
     *  SimpleOrderedHashtable, or null if it did not have one.
     */
    synchronized public Object put(Object key, Object value) {
        int i = orderedKeys.indexOf(key);
        if (i == -1)  {
            //  Add new name/value pair.
            orderedKeys.addElement(key); // insert (append) to the end of the list
        } else {
            //  Replace name/value pair.
            orderedKeys.setElementAt(key, i);
        }
        return hashTable.put(key, value);
    }

    /**
     *  Returns the value to which the specified key is mapped in this
     *  hashtable.
     *  @param key is a key in the SimpleOrderedHashtable.
     *  @return the value to which the key is mapped in this hashtable; null if
     *  the key is not mapped to any value in this hashtable.
     */
    synchronized public Object get(Object key) {
        return hashTable.get(key);
    }

    /**
     *  Returns an enumeration of the keys in this SimpleOrderedHashtable.
     *  @return an enumeration of the keys in this SimpleOrderedHashtable.
     */
    synchronized public Enumeration keys() {
        return orderedKeys.elements();
    }

    /**
     *  Returns an enumeration of the elements in this SimpleOrderedHashtable.
     *  @return an enumeration of the elements in this SimpleOrderedHashtable.
     */
    synchronized public Enumeration elements() {
        int s = hashTable.size();
        Vector elements = new Vector(s);
        for (int i=0; i<s; i++) {
            elements.addElement(elementAt(i));
        }
        return elements.elements();
    }

    /**
     *  Returns the component at the specified index.
     *  @param index is an index into this SimpleOrderedHashtable.
     *  @return the <code>Object</code> component at the specified index.
     *  @throws ArrayIndexOutOfBoundsException if index is out of bounds.
     */
    synchronized public Object elementAt(int index)
            throws ArrayIndexOutOfBoundsException {
        Object key = orderedKeys.elementAt(index);
        return hashTable.get(key);
    }

    /**
     *  Returns the key at the specified index.
     *  @param index is an index into this SimpleOrderedHashtable.
     *  @return the <code>Object</code> key at the specified index.
     *  @throws ArrayIndexOutOfBoundsException if index is out of bounds.
     */
    synchronized public Object keyAt(int index)
            throws ArrayIndexOutOfBoundsException {
        return orderedKeys.elementAt(index);
    }

    /**
     *  Returns the index of the specified <code>Object</code>.
     *  @param key is a key in the SimpleOrderedHashtable.
     *  @return the index of the specified <code>Object</code>.
     */
    synchronized public int getIndex(Object key) {
        return orderedKeys.indexOf(key);
    }

    /**
     *  Removes the key (and its corresponding value) from this hashtable. This
     *  method does nothing if the key is not in the hashtable.
     *  @param key is the key that needs to be removed.
     */
    synchronized public void remove(Object key) {
        orderedKeys.removeElement(key);
        hashTable.remove(key);
    }

    /**
     * Removes an element at the specified index.
     * @param i is the index of the element to remove.
     */
    synchronized public void removeElementAt(int i) {
        Object key = orderedKeys.elementAt(i);
        orderedKeys.removeElementAt(i);
        hashTable.remove(key);
    }

    /**
     *  Clears this SimpleOrderedHashtable so that it contains no keys.
     */
    synchronized public void clear() {
        orderedKeys.removeAllElements();
        hashTable.clear();
    }

    /**
     *  Returns the number of components in this SimpleOrderedHashtable.
     *  @return the number of components in this vector.
     */
    synchronized public int size() {
        return orderedKeys.size();
    }

    /**
     * Recomputes the SimpleOrderedHashtable capacity.
     * @param capacity is the capacity to ensure.
     */
    synchronized public void ensureCapacity(int capacity) {
        orderedKeys.ensureCapacity(capacity);
    }
}
 
<think>嗯,用户问的是数据库设计中为什么键列必须是模式的有序前缀。这个问题涉及到数据库索引的核心原理,需要从数据结构层面解释清楚。 用户可能正在学习数据库索引优化,或者遇到了实际设计问题。从ta强调"ordered prefix"这个术语来看,应该已经接触过B+树等概念,但需要更直观的理解。ta的真实需求可能是想确认设计规范背后的理论依据,避免在实际项目中犯错。 让我拆解几个关键点:首先,前缀特性保证了索引键的连续性,就像字典的字母排序规则。如果允许跳跃列,就像让你查"以C开头第三个字母是T的单词"——必须扫描所有C开头的词条,失去了索引的快速定位意义。 其次,有序性关系到B+树的二分查找效率。想象一下电话簿如果先按名后按姓排序,找"John Smith"时就能快速定位J区再找Smith;若无序则需全表扫描。这也是为什么联合索引(a,b)能高效处理WHERE a=1 AND b=2,却无法优化单独查b=2的查询。 最后补充一个容易被忽视的点:存储引擎的物理存储结构。当键列连续有序时,相邻数据在磁盘上往往物理相邻,范围查询(如WHERE date BETWEEN ...)能触发顺序读写,比随机IO快几个数量级。这也是为什么ClickHouse等OLAP数据库特别强调排序键的设计。 用户可能还想知道如何解决非前缀查询的优化问题,这正好作为后续问题的切入点。比如覆盖索引或物化视图就是常见解决方案,但需要权衡存储空间和更新开销。</think>在数据库设计中,要求键列(Key Columns)必须是模式(Schema)的**有序前缀**(Ordered Prefix),主要源于索引结构(尤其是B+树索引)的工作原理和查询优化需求。以下是关键原因: --- ### 一、索引结构的内在要求 1. **B+树索引的排序规则** 大多数数据库(如MySQL/InnoDB、PostgreSQL)使用B+树索引。B+树要求数据**按索引键的顺序物理存储**。 - 若键列为`(col1, col2, col3)`,数据会先按`col1`排序,`col1`相同时按`col2`排序,依此类推。 - **非前缀查询(如跳过`col1`直接查`col2`)**: 由于`col2`的值在索引中**全局无序**(仅局部有序),数据库需扫描整个索引或回表查询,效率低下[^1]。 2. **前缀匹配原则** B+树索引仅能高效支持**最左前缀匹配**(Leftmost Prefix Matching)。例如: - 索引`(a, b, c)` 可优化: ```sql WHERE a=1 AND b=2; -- ✅ 有效 WHERE a=1; -- ✅ 有效 WHERE b=2 AND c=3; -- ❌ 无效(缺少a) WHERE a=1 AND c=3; -- ✅ 部分有效(仅用a过滤,c无法参与索引过滤) ``` --- ### 二、数据存储与查询性能 1. **减少I/O开销** - 有序前缀保证索引键的**连续性**,范围查询(如`WHERE a>10 AND a<20`)可直接定位到连续磁盘块。 - 若键列非前缀(如索引`(a, c, b)`),查询`WHERE a=1 AND b=2`时,`b`的过滤需额外扫描无序数据。 2. **避免全表扫描** 非前缀查询可能迫使优化器放弃索引(如上述`WHERE b=2`),转而执行全表扫描(Full Table Scan),时间复杂度从$O(\log n)$退化为$O(n)$[^2]。 --- ### 三、设计实践与解决方案 1. **正确设计键列顺序** - **高频查询优先**:将频繁出现在`WHERE`子句的列放在左侧。 - **基数递减**:高基数(唯一值多)的列放左侧(如`user_id`放`status`前)。 2. **覆盖索引优化** 若需查询非前缀列,可将其包含在索引中(非键列,但存储在叶子节点): ```sql CREATE INDEX idx_cover ON table (a, b) INCLUDE (c); -- SQL Server语法 CREATE INDEX idx_cover ON table (a, b, c); -- MySQL中c作为非键列 ``` 此时查询`SELECT c FROM table WHERE a=1 AND b=2`可直接从索引返回数据,避免回表。 3. **使用多列索引替代跳跃查询** 若需跳过前缀列查询(如`WHERE b=2`),需单独为`b`创建索引: ```sql CREATE INDEX idx_b ON table (b); ``` --- ### 四、反例说明 假设表`sales`含列`(region, product, date)`,索引为`(region, date)`: - ✅ 高效查询: ```sql SELECT * FROM sales WHERE region='Asia' AND date='2023-01-01'; ``` - ❌ 低效查询: ```sql SELECT * FROM sales WHERE product='Laptop'; -- 需全表扫描 ``` **解决方案**:为`product`单独建索引,或创建新索引`(product, region, date)`。 --- ### 总结 | 关键原则 | 原因 | |-------------------------|----------------------------------------------------------------------| | **有序前缀** | B+树索引的物理排序依赖键列顺序 | | **最左前缀匹配** | 索引仅能高效过滤从左侧开始的连续列 | | **避免全表扫描** | 非前缀查询导致索引失效 | | **覆盖索引优化** | 将非过滤列包含在索引中,避免回表 | > 数据库设计时,应结合查询模式调整键列顺序,确保高频查询条件能利用索引的最左前缀特性。若业务需多维度查询,可考虑列式存储(如OLAP数据库)或额外索引[^3]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值