【问题标题】:Java: how to store data triple in a list?Java:如何在列表中存储数据三元组?
【发布时间】:2011-05-15 19:55:48
【问题描述】:

在 java 中将数据三元组存储在列表中的最佳方法是什么?

[a, b, c]
[a, b, c]
... 

我通常将 HashMap 用于一对数据键 + 值。我应该使用 HashMap + Arraylist 吗?还是 ArrayList + ArrayList ?

谢谢

【问题讨论】:

  • 或者创建一个包含三个对象的新类。你用它做什么?

标签: java


【解决方案1】:
public class Triplet<T, U, V> {

    private final T first;
    private final U second;
    private final V third;

    public Triplet(T first, U second, V third) {
        this.first = first;
        this.second = second;
        this.third = third;
    }

    public T getFirst() { return first; }
    public U getSecond() { return second; }
    public V getThird() { return third; }
}

并实例化列表:

List<Triplet<String, Integer, Integer>> = new ArrayList<>();

【讨论】:

  • 您应该将abc 设为final。
  • equals 和 hashCode 在这里可能还不错
  • 如果 a、b、c 不是私有的,为什么还要创建 getter?
  • @SteveKuo 为什么它必须是最终的?
  • @AmiHollander 在之前的编辑中,这些字段不是私有的。现在它有点多余,但可能仍然有助于传达它们是最终的(而不是暗示缺乏 setter)。
【解决方案2】:
public class Triple<F, S, T> {

    public final F first;
    public final S second;
    public final T third;

    public Triple(F first, S second, T third) {
        this.first = first;
        this.second = second;
        this.third = third;
    }

    @Override
    public boolean equals(Object o) {
        if (!(o instanceof Triple)) {
            return false;
        }
        Triple<?, ?, ?> p = (Triple<?, ?, ?>) o;
        return first.equals(p.first) && second.equals(p.second) && third.equals(p.third);
    }

    private static boolean equals(Object x, Object y) {
        return (x == null && y == null) || (x != null && x.equals(y));
    }

    @Override
    public int hashCode() {
        return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode()) ^ (third == null ? 0 : third.hashCode());
    }

    public static <F, S, T> Triple <F, S, T> create(F f, S s, T t) {
        return new Triple<F, S, T>(f, s, t);
    }
}

【讨论】:

  • 这个类的hashCode方法很容易发现冲突。考虑创建三个实例()。第一个是 [1,0,0],第二个是 [0,1,0],第三个是 [0,0,1]。尽管它们是不同的三元组,但它们的 hashCode 都等于 1。
【解决方案3】:

基于TripleImmutableTriple,不依赖Apache 代码的导入。 L、M 和 R 类型必须实现接口 Comparable。

Triple.java

import java.io.Serializable;

/**
 * <p>A triple consisting of three elements.</p>
 * <p/>
 * <p>This class is an abstract implementation defining the basic API. It refers to the elements as
 * 'left', 'middle' and 'right'.</p>
 * <p/>
 * <p>Subclass implementations may be mutable or immutable. However, there is no restriction on the
 * type of the stored objects that may be stored. If mutable objects are stored in the triple, then
 * the triple itself effectively becomes mutable.</p>
 *
 * @param <L>
 *         the left element type
 * @param <M>
 *         the middle element type
 * @param <R>
 *         the right element type
 *
 * @version $Id: Triple.java 1557584 2014-01-12 18:26:49Z britter $
 * @since 3.2
 */
public abstract class Triple<L, M, R>
        implements Comparable<Triple<L, M, R>>, Serializable
{

    /**
     * Serialization version
     */
    private static final long serialVersionUID = 1L;


    /**
     * <p>Obtains an immutable triple of from three objects inferring the generic types.</p>
     * <p/>
     * <p>This factory allows the triple to be created using inference to obtain the generic
     * types.</p>
     *
     * @param <L>
     *         the left element type
     * @param <M>
     *         the middle element type
     * @param <R>
     *         the right element type
     * @param left
     *         the left element, may be null
     * @param middle
     *         the middle element, may be null
     * @param right
     *         the right element, may be null
     *
     * @return a triple formed from the three parameters, not null
     */
    public static <L, M, R> Triple<L, M, R> of(
            final L left,
            final M middle,
            final R right)
    {
        return new ImmutableTriple<L, M, R>(left, middle, right);
    }

    //-----------------------------------------------------------------------


    /**
     * <p>Gets the left element from this triple.</p>
     *
     * @return the left element, may be null
     */
    public abstract L getLeft();

    /**
     * <p>Gets the middle element from this triple.</p>
     *
     * @return the middle element, may be null
     */
    public abstract M getMiddle();

    /**
     * <p>Gets the right element from this triple.</p>
     *
     * @return the right element, may be null
     */
    public abstract R getRight();

    //-----------------------------------------------------------------------


    /**
     * <p>Compares the triple based on the left element, followed by the middle element, finally the
     * right element. The types must be {@code Comparable}.</p>
     *
     * @param other
     *         the other triple, not null
     *
     * @return negative if this is less, zero if equal, positive if greater
     */
    @Override
    public int compareTo(final Triple<L, M, R> other)
    {
        @SuppressWarnings("unchecked") // assume this can be done; if not throw CCE as per Javadoc
        final Comparable<Object> comparableLeft = (Comparable<Object>) getLeft();
        int cmpLeft = comparableLeft.compareTo(other.getLeft());

        if (cmpLeft != 0) {
            return cmpLeft;
        }

        @SuppressWarnings("unchecked") // assume this can be done; if not throw CCE as per Javadoc
        final Comparable<Object> comparableMidle = (Comparable<Object>) getMiddle();
        int cmpMidle = comparableMidle.compareTo(other.getMiddle());

        if (cmpMidle != 0) {
            return cmpMidle;
        }

        @SuppressWarnings("unchecked") // assume this can be done; if not throw CCE as per Javadoc
        final Comparable<Object> comparableRight = (Comparable<Object>) getRight();
        int cmpRight = comparableRight.compareTo(other.getRight());

        return cmpRight;
    }


    /**
     * <p>Compares this triple to another based on the three elements.</p>
     *
     * @param obj
     *         the object to compare to, null returns false
     *
     * @return true if the elements of the triple are equal
     */
    @Override
    public boolean equals(final Object obj)
    {
        if (obj == null) {
            return false;
        }
        if (obj == this) {
            return true;
        }
        if (obj instanceof Triple<?, ?, ?>) {
            final Triple<?, ?, ?> other = (Triple<?, ?, ?>) obj;


            return equals(getLeft(), other.getLeft()) &&
                    equals(getMiddle(), other.getMiddle()) &&
                    equals(getRight(), other.getRight());
        }
        return false;
    }


    private boolean equals(
            final Object object1,
            final Object object2)
    {
        return !(object1 == null || object2 == null) &&
                (object1 == object2 || object1.equals(object2));
    }


    /**
     * <p>Returns a suitable hash code.</p>
     *
     * @return the hash code
     */
    @Override
    public int hashCode()
    {
        return (getLeft() == null ? 0 : getLeft().hashCode()) ^
                (getMiddle() == null ? 0 : getMiddle().hashCode()) ^
                (getRight() == null ? 0 : getRight().hashCode());
    }


    /**
     * <p>Returns a String representation of this triple using the format {@code
     * ($left, $middle, $right)}.</p>
     *
     * @return a string describing this object, not null
     */
    @Override
    public String toString()
    {
        return new StringBuilder().append('(')
                .append(getLeft())
                .append(',')
                .append(getMiddle())
                .append(',')
                .append(getRight())
                .append(')')
                .toString();
    }


    /**
     * <p>Formats the receiver using the given format.</p>
     * <p/>
     * <p>This uses {@link java.util.Formattable} to perform the formatting. Three variables may be
     * used to embed the left and right elements. Use {@code %1$s} for the left element, {@code
     * %2$s} for the middle and {@code %3$s} for the right element. The default format used by
     * {@code toString()} is {@code (%1$s,%2$s,%3$s)}.</p>
     *
     * @param format
     *         the format string, optionally containing {@code %1$s}, {@code %2$s} and {@code %3$s},
     *         not null
     *
     * @return the formatted string, not null
     */
    public String toString(final String format)
    {
        return String.format(format, getLeft(), getMiddle(), getRight());
    }
}

ImmutableTriple.java

public final class ImmutableTriple<L, M, R> extends Triple<L, M, R> {

    /** Serialization version */
    private static final long serialVersionUID = 1L;

    /** Left object */
    public final L left;
    /** Middle object */
    public final M middle;
    /** Right object */
    public final R right;

    /**
     * <p>Obtains an immutable triple of from three objects inferring the generic types.</p>
     *
     * <p>This factory allows the triple to be created using inference to
     * obtain the generic types.</p>
     *
     * @param <L> the left element type
     * @param <M> the middle element type
     * @param <R> the right element type
     * @param left  the left element, may be null
     * @param middle  the middle element, may be null
     * @param right  the right element, may be null
     * @return a triple formed from the three parameters, not null
     */
    public static <L, M, R> ImmutableTriple<L, M, R> of(final L left, final M middle, final R right) {
        return new ImmutableTriple<L, M, R>(left, middle, right);
    }

    /**
     * Create a new triple instance.
     *
     * @param left  the left value, may be null
     * @param middle the middle value, may be null
     * @param right  the right value, may be null
     */
    public ImmutableTriple(final L left, final M middle, final R right) {
        super();
        this.left = left;
        this.middle = middle;
        this.right = right;
    }

    //-----------------------------------------------------------------------
    /**
     * {@inheritDoc}
     */
    @Override
    public L getLeft() {
        return left;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public M getMiddle() {
        return middle;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public R getRight() {
        return right;
    }
}

【讨论】:

    【解决方案4】:

    如果您希望同时跟踪四个或更多对象,请寻找可以缩放的对象。

    如果您决定要使用 Triple 的概念,为了获得一些想法,您可以遵循 Sun 的 javac.util.Pair 模式:

    public class Pair<A, B> {
    
        public final A fst;
        public final B snd;
    
        public Pair(A fst, B snd) {
            this.fst = fst;
            this.snd = snd;
        }
    
        public String toString() {
            return "Pair[" + fst + "," + snd + "]";
        }
    
        private static boolean equals(Object x, Object y) {
            return (x == null && y == null) || (x != null && x.equals(y));
        }
    
        public boolean equals(Object other) {
            return
                other instanceof Pair<?,?> &&
                equals(fst, ((Pair<?,?>)other).fst) &&
                equals(snd, ((Pair<?,?>)other).snd);
        }
    
        public int hashCode() {
            if (fst == null) return (snd == null) ? 0 : snd.hashCode() + 1;
            else if (snd == null) return fst.hashCode() + 2;
            else return fst.hashCode() * 17 + snd.hashCode();
        }
    
        public static <A,B> Pair<A,B> of(A a, B b) {
            return new Pair<A,B>(a,b);
        }
    }
    

    或关注谷歌的android.util.Pair

    public class Pair<F, S> {
        public final F first;
        public final S second;
    
        public Pair(F first, S second) {
            this.first = first;
            this.second = second;
        }
    
        public boolean equals(Object o) {
            if (o == this) return true;
            if (!(o instanceof Pair)) return false;
            final Pair<F, S> other;
            try {
                other = (Pair<F, S>) o;
            } catch (ClassCastException e) {
                return false;
            }
            return first.equals(other.first) && second.equals(other.second);
        }
    
        public int hashCode() {
            int result = 17;
            result = 31 * result + first.hashCode();
            result = 31 * result + second.hashCode();
            return result;
        }
        //...
    }
    

    【讨论】:

      【解决方案5】:

      我似乎同意 whirlwin。

      虽然我现在找不到证据,但普遍的共识规定参数化类型不应超过 2。这就是为什么我能想到的 Java 集合库中的每个类都最多有两个参数化类型。

      当您拥有三种参数化类型时,代码会变得混乱。太多了 >,

      我建议 whirlwin 所说的并创建一个包含您需要的三种特定类型的对象。

      【讨论】:

        【解决方案6】:

        Kotlin 实现了一个名为 Triple 的类:

        /**
         * Represents a triad of values
        *
        * There is no meaning attached to values in this class, it can be used for any purpose.
        * Triple exhibits value semantics, i.e. two triples are equal if all three components are equal.
        * An example of decomposing it into values:
        * @sample samples.misc.Tuples.tripleDestructuring
        *
        * @param A type of the first value.
        * @param B type of the second value.
        * @param C type of the third value.
        * @property first First value.
        * @property second Second value.
        * @property third Third value.
        */
        
        public data class Triple<out A, out B, out C>(
           public val first: A,
           public val second: B,
           public val third: C
        ) : Serializable {
        
           /**
            * Returns string representation of the [Triple] including its [first],    [second] and [third] values.
            */
           public override fun toString(): String = "($first, $second, $third)"
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-04-25
          • 1970-01-01
          • 2016-07-15
          • 2021-03-03
          • 1970-01-01
          • 2013-04-15
          • 1970-01-01
          相关资源
          最近更新 更多