001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.commons.function; 018 019import static org.apache.juneau.commons.utils.Utils.*; 020 021import java.util.Optional; 022 023import org.apache.juneau.commons.lang.*; 024 025/** 026 * Represents an immutable tuple containing two values. 027 * 028 * <p> 029 * This class provides a simple wrapper for two values that properly implements 030 * {@link #equals(Object)} and {@link #hashCode()} based on content rather than identity. 031 * This is particularly useful for HashMap composite keys when you need to combine multiple 032 * values into a single key. 033 * 034 * <h5 class='section'>Features:</h5> 035 * <ul class='spaced-list'> 036 * <li>Immutable - values cannot be changed after construction 037 * <li>Content-based equality - uses {@link org.apache.juneau.commons.utils.Utils#eq(Object, Object)} for comparison 038 * <li>Content-based hashing - uses {@link HashCode#of(Object...)} for hash code computation 039 * <li>Array support - properly handles arrays with content-based equality and hashing 040 * </ul> 041 * 042 * <h5 class='section'>Array Support:</h5> 043 * <p> 044 * Unlike using arrays directly as HashMap keys (which use identity-based equality), this class properly 045 * handles arrays by using content-based equality and hashing via {@link HashCode#of(Object...)} which 046 * internally uses {@link java.util.Arrays#hashCode(Object[])} for arrays. This means two Tuple2 instances 047 * containing arrays with the same content will be considered equal. 048 * 049 * <h5 class='section'>Use Cases:</h5> 050 * <ul class='spaced-list'> 051 * <li>Composite keys for HashMap lookups (e.g., combining two identifiers) 052 * <li>Using arrays in composite keys with proper content-based equality 053 * <li>Returning multiple values from methods (alternative to creating a custom class) 054 * <li>Grouping related values together for processing 055 * </ul> 056 * 057 * <h5 class='section'>Usage:</h5> 058 * <p class='bjava'> 059 * <jc>// Using arrays in composite keys</jc> 060 * Map<Tuple2<String,int[]>,Result> <jv>map</jv> = <jk>new</jk> HashMap<>(); 061 * <jv>map</jv>.put(Tuple2.<jsm>of</jsm>(<js>"key"</js>, <jk>new</jk> <jk>int</jk>[]{1,2,3}), <jv>result1</jv>); 062 * Result <jv>r</jv> = <jv>map</jv>.get(Tuple2.<jsm>of</jsm>(<js>"key"</js>, <jk>new</jk> <jk>int</jk>[]{1,2,3})); <jc>// Works correctly!</jc> 063 * 064 * <jc>// Simple composite key</jc> 065 * Map<Tuple2<String,Integer>,String> <jv>cache</jv> = <jk>new</jk> HashMap<>(); 066 * <jv>cache</jv>.put(Tuple2.<jsm>of</jsm>(<js>"user"</js>, 12345), <js>"John Doe"</js>); 067 * String <jv>name</jv> = <jv>cache</jv>.get(Tuple2.<jsm>of</jsm>(<js>"user"</js>, 12345)); <jc>// Returns "John Doe"</jc> 068 * </p> 069 * 070 * <h5 class='section'>Thread Safety:</h5> 071 * <p> 072 * This class is immutable and therefore thread-safe. Multiple threads can safely access a Tuple2 instance 073 * concurrently without synchronization. 074 * 075 * <h5 class='section'>See Also:</h5><ul> 076 * <li class='jc'>{@link Tuple1} - Single-value tuple 077 * <li class='jc'>{@link Tuple3} - Three-value tuple 078 * <li class='jc'>{@link Tuple4} - Four-value tuple 079 * <li class='jc'>{@link Tuple5} - Five-value tuple 080 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauCommonsBasics">juneau-commons Basics</a> 081 * </ul> 082 * 083 * @param <A> The type of the first value in this tuple. 084 * @param <B> The type of the second value in this tuple. 085 */ 086public class Tuple2<A,B> { 087 088 /** 089 * Creates a new tuple containing the specified two values. 090 * 091 * <h5 class='section'>Example:</h5> 092 * <p class='bjava'> 093 * Tuple2<String,Integer> <jv>tuple</jv> = Tuple2.<jsm>of</jsm>(<js>"name"</js>, 42); 094 * Tuple2<String[],int[]> <jv>arrayTuple</jv> = Tuple2.<jsm>of</jsm>( 095 * <jk>new</jk> String[]{<js>"a"</js>, <js>"b"</js>}, 096 * <jk>new</jk> <jk>int</jk>[]{1, 2} 097 * ); 098 * </p> 099 * 100 * @param <A> The type of the first value. 101 * @param <B> The type of the second value. 102 * @param a The first value. 103 * @param b The second value. 104 * @return A new tuple containing the specified values. 105 */ 106 public static <A,B> Tuple2<A,B> of(A a, B b) { 107 return new Tuple2<>(a, b); 108 } 109 110 private final A a; 111 private final B b; 112 private final int hashCode; 113 114 /** 115 * Constructor. 116 * 117 * @param a Object 1. 118 * @param b Object 2. 119 */ 120 public Tuple2(A a, B b) { 121 this.a = a; 122 this.b = b; 123 this.hashCode = h(a, b); 124 } 125 126 @Override /* Overridden from Object */ 127 public boolean equals(Object o) { 128 return o instanceof Tuple2<?,?> o2 && eq(this, o2, (x, y) -> eq(x.a, y.a) && eq(x.b, y.b)); 129 } 130 131 /** 132 * Returns the first value in this tuple. 133 * 134 * <h5 class='section'>Example:</h5> 135 * <p class='bjava'> 136 * Tuple2<String,Integer> <jv>tuple</jv> = Tuple2.<jsm>of</jsm>(<js>"hello"</js>, 42); 137 * String <jv>first</jv> = <jv>tuple</jv>.getA(); <jc>// Returns "hello"</jc> 138 * </p> 139 * 140 * @return The first value in this tuple. 141 */ 142 public A getA() { return a; } 143 144 /** 145 * Returns the second value in this tuple. 146 * 147 * <h5 class='section'>Example:</h5> 148 * <p class='bjava'> 149 * Tuple2<String,Integer> <jv>tuple</jv> = Tuple2.<jsm>of</jsm>(<js>"hello"</js>, 42); 150 * Integer <jv>second</jv> = <jv>tuple</jv>.getB(); <jc>// Returns 42</jc> 151 * </p> 152 * 153 * @return The second value in this tuple. 154 */ 155 public B getB() { return b; } 156 157 /** 158 * Returns the first value in this tuple wrapped in an {@link Optional}. 159 * 160 * <h5 class='section'>Example:</h5> 161 * <p class='bjava'> 162 * Tuple2<String,Integer> <jv>tuple</jv> = Tuple2.<jsm>of</jsm>(<js>"hello"</js>, 42); 163 * Optional<String> <jv>first</jv> = <jv>tuple</jv>.optA(); <jc>// Returns Optional.of("hello")</jc> 164 * 165 * Tuple2<String,Integer> <jv>tuple2</jv> = Tuple2.<jsm>of</jsm>(<jk>null</jk>, 42); 166 * Optional<String> <jv>first2</jv> = <jv>tuple2</jv>.optA(); <jc>// Returns Optional.empty()</jc> 167 * </p> 168 * 169 * @return The first value wrapped in an Optional, or Optional.empty() if the value is null. 170 */ 171 public Optional<A> optA() { 172 return Optional.ofNullable(a); 173 } 174 175 /** 176 * Returns the second value in this tuple wrapped in an {@link Optional}. 177 * 178 * <h5 class='section'>Example:</h5> 179 * <p class='bjava'> 180 * Tuple2<String,Integer> <jv>tuple</jv> = Tuple2.<jsm>of</jsm>(<js>"hello"</js>, 42); 181 * Optional<Integer> <jv>second</jv> = <jv>tuple</jv>.optB(); <jc>// Returns Optional.of(42)</jc> 182 * 183 * Tuple2<String,Integer> <jv>tuple2</jv> = Tuple2.<jsm>of</jsm>(<js>"hello"</js>, <jk>null</jk>); 184 * Optional<Integer> <jv>second2</jv> = <jv>tuple2</jv>.optB(); <jc>// Returns Optional.empty()</jc> 185 * </p> 186 * 187 * @return The second value wrapped in an Optional, or Optional.empty() if the value is null. 188 */ 189 public Optional<B> optB() { 190 return Optional.ofNullable(b); 191 } 192 193 @Override /* Overridden from Object */ 194 public int hashCode() { 195 return hashCode; 196 } 197}